2010-01-14 42 views
17

Tôi muốn xây dựng bộ tổng hợp cho iPhone. Tôi hiểu rằng có thể sử dụng các đơn vị âm thanh tùy chỉnh cho iPhone. Thoạt nhìn, điều này nghe có vẻ hứa hẹn, vì có rất nhiều tài nguyên lập trình Audio Unit có sẵn. Tuy nhiên, bằng cách sử dụng các đơn vị âm thanh tùy chỉnh trên iPhone có vẻ hơi phức tạp (xem: http://lists.apple.com/archives/Coreaudio-api/2008/Nov/msg00262.html)nơi bắt đầu với tổng hợp âm thanh trên iPhone

Điều này có vẻ như một thứ mà mọi người phải làm, nhưng tìm kiếm đơn giản cho "tổng hợp âm thanh iphone" t bật lên bất cứ điều gì dọc theo dòng của một hướng dẫn tốt đẹp và dễ dàng hoặc bộ công cụ được đề nghị.

Vì vậy, bất kỳ ai ở đây đều có trải nghiệm tổng hợp âm thanh trên iPhone? Các đơn vị âm thanh tùy chỉnh có phải là cách để đi hay không, cách tiếp cận đơn giản hơn tôi nên cân nhắc?

+0

Trên Mac OS việc sử dụng âm thanh đơn vị thực sự thuận tiện và cung cấp một tấn các chức năng. Thật không may có vẻ như hầu như không có gì được thực hiện trên iPhone. Có một vài AU cơ bản nhưng không nơi nào gần những gì có sẵn trên hệ điều hành MacOS. Tôi đã đệ trình lỗi một năm trước, nhưng chưa có tiến triển nào. –

+0

Tôi googled đơn vị âm thanh iphone và tìm thấy ai đó nói về cách sử dụng tùy chỉnh của AU trên iphone, nhưng có vẻ như một chút lông. – morgancodes

Trả lời

21

Tôi cũng đang điều tra điều này. Tôi nghĩ rằng AudioQueue API có lẽ là con đường để đi.

Dưới đây là điều tôi nhận được, có vẻ như không sao.

File: BleepMachine.h

// 
// BleepMachine.h 
// WgHeroPrototype 
// 
// Created by Andy Buchanan on 05/01/2010. 
// Copyright 2010 Andy Buchanan. All rights reserved. 
// 

#include <AudioToolbox/AudioToolbox.h> 

// Class to implement sound playback using the AudioQueue API's 
// Currently just supports playing two sine wave tones, one per 
// stereo channel. The sound data is liitle-endian signed 16-bit @ 44.1KHz 
// 
class BleepMachine 
{ 
    static void staticQueueCallback(void* userData, AudioQueueRef outAQ, AudioQueueBufferRef outBuffer) 
    { 
     BleepMachine* pThis = reinterpret_cast<BleepMachine*> (userData); 
     pThis->queueCallback(outAQ, outBuffer); 
    } 
    void queueCallback(AudioQueueRef outAQ, AudioQueueBufferRef outBuffer); 

    AudioStreamBasicDescription m_outFormat; 

    AudioQueueRef m_outAQ; 

    enum 
    { 
     kBufferSizeInFrames = 512, 
     kNumBuffers = 4, 
     kSampleRate = 44100, 
    }; 

    AudioQueueBufferRef m_buffers[kNumBuffers]; 

    bool m_isInitialised; 

    struct Wave 
    { 
     Wave(): volume(1.f), phase(0.f), frequency(0.f), fStep(0.f) {} 
     float volume; 
     float phase; 
     float frequency; 
     float fStep; 
    }; 

    enum 
    { 
     kLeftWave = 0, 
     kRightWave = 1, 
     kNumWaves, 
    }; 

    Wave m_waves[kNumWaves]; 

public: 
    BleepMachine(); 
    ~BleepMachine(); 

    bool Initialise(); 
    void Shutdown(); 

    bool Start(); 
    bool Stop(); 

    bool SetWave(int id, float frequency, float volume); 
}; 

// Notes by name. Integer value is number of semitones above A. 
enum Note 
{ 
    A  = 0, 
    Asharp, 
    B, 
    C, 
    Csharp, 
    D, 
    Dsharp, 
    E, 
    F, 
    Fsharp, 
    G, 
    Gsharp, 

    Bflat = Asharp, 
    Dflat = Csharp, 
    Eflat = Dsharp, 
    Gflat = Fsharp, 
    Aflat = Gsharp, 
}; 

// Helper function calculates fundamental frequency for a given note 
float CalculateFrequencyFromNote(SInt32 semiTones, SInt32 octave=4); 
float CalculateFrequencyFromMIDINote(SInt32 midiNoteNumber); 

File: BleepMachine.mm

// 
// BleepMachine.mm 
// WgHeroPrototype 
// 
// Created by Andy Buchanan on 05/01/2010. 
// Copyright 2010 Andy Buchanan. All rights reserved. 
// 

#include "BleepMachine.h" 

void BleepMachine::queueCallback(AudioQueueRef outAQ, AudioQueueBufferRef outBuffer) 
{ 
    // Render the wave 

    // AudioQueueBufferRef is considered "opaque", but it's a reference to 
    // an AudioQueueBuffer which is not. 
    // All the samples manipulate this, so I'm not quite sure what they mean by opaque 
    // saying.... 
    SInt16* coreAudioBuffer = (SInt16*)outBuffer->mAudioData; 

    // Specify how many bytes we're providing 
    outBuffer->mAudioDataByteSize = kBufferSizeInFrames * m_outFormat.mBytesPerFrame; 

    // Generate the sine waves to Signed 16-Bit Stero interleaved (Little Endian) 
    float volumeL = m_waves[kLeftWave].volume; 
    float volumeR = m_waves[kRightWave].volume; 
    float phaseL = m_waves[kLeftWave].phase; 
    float phaseR = m_waves[kRightWave].phase; 
    float fStepL = m_waves[kLeftWave].fStep; 
    float fStepR = m_waves[kRightWave].fStep; 

    for(int s=0; s<kBufferSizeInFrames*2; s+=2) 
    { 
     float sampleL = (volumeL * sinf(phaseL)); 
     float sampleR = (volumeR * sinf(phaseR)); 

     short sampleIL = (int)(sampleL * 32767.0); 
     short sampleIR = (int)(sampleR * 32767.0); 

     coreAudioBuffer[s] = sampleIL; 
     coreAudioBuffer[s+1] = sampleIR; 

     phaseL += fStepL; 
     phaseR += fStepR; 
    } 

    m_waves[kLeftWave].phase = fmodf(phaseL, 2 * M_PI); // Take modulus to preserve precision 
    m_waves[kRightWave].phase = fmodf(phaseR, 2 * M_PI); 

    // Enqueue the buffer 
    AudioQueueEnqueueBuffer(m_outAQ, outBuffer, 0, NULL); 
} 

bool BleepMachine::SetWave(int id, float frequency, float volume) 
{ 
    if ((id < kLeftWave) || (id >= kNumWaves)) return false; 

    Wave& wave = m_waves[ id ]; 

    wave.volume = volume; 
    wave.frequency = frequency; 
    wave.fStep = 2 * M_PI * frequency/kSampleRate; 

    return true; 
} 

bool BleepMachine::Initialise() 
{ 
    m_outFormat.mSampleRate = kSampleRate; 
    m_outFormat.mFormatID = kAudioFormatLinearPCM; 
    m_outFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    m_outFormat.mFramesPerPacket = 1; 
    m_outFormat.mChannelsPerFrame = 2; 
    m_outFormat.mBytesPerPacket = m_outFormat.mBytesPerFrame = sizeof(UInt16) * 2; 
    m_outFormat.mBitsPerChannel = 16; 
    m_outFormat.mReserved = 0; 

    OSStatus result = AudioQueueNewOutput(
              &m_outFormat, 
              BleepMachine::staticQueueCallback, 
              this, 
              NULL, 
              NULL, 
              0, 
              &m_outAQ 
             ); 

    if (result < 0) 
    { 
     printf("ERROR: %d\n", (int)result); 
     return false; 
    } 

    // Allocate buffers for the audio 
    UInt32 bufferSizeBytes = kBufferSizeInFrames * m_outFormat.mBytesPerFrame; 

    for (int buf=0; buf<kNumBuffers; buf++) 
    { 
     OSStatus result = AudioQueueAllocateBuffer(m_outAQ, bufferSizeBytes, &m_buffers[ buf ]); 
     if (result) 
     { 
      printf("ERROR: %d\n", (int)result); 
      return false; 
     } 

     // Prime the buffers 
     queueCallback(m_outAQ, m_buffers[ buf ]); 
    } 

    m_isInitialised = true; 
    return true; 
} 

void BleepMachine::Shutdown() 
{ 
    Stop(); 

    if (m_outAQ) 
    { 
     // AudioQueueDispose also chucks any audio buffers it has 
     AudioQueueDispose(m_outAQ, true); 
    } 

    m_isInitialised = false; 
} 

BleepMachine::BleepMachine() 
: m_isInitialised(false), m_outAQ(0) 
{ 
    for (int buf=0; buf<kNumBuffers; buf++) 
    { 
     m_buffers[ buf ] = NULL; 
    } 
} 

BleepMachine::~BleepMachine() 
{ 
    Shutdown(); 
} 

bool BleepMachine::Start() 
{ 
    OSStatus result = AudioQueueSetParameter(m_outAQ, kAudioQueueParam_Volume, 1.0); 
    if (result) printf("ERROR: %d\n", (int)result); 

    // Start the queue 
    result = AudioQueueStart(m_outAQ, NULL); 
    if (result) printf("ERROR: %d\n", (int)result); 

    return true; 
} 

bool BleepMachine::Stop() 
{ 
    OSStatus result = AudioQueueStop(m_outAQ, true); 
    if (result) printf("ERROR: %d\n", (int)result); 

    return true; 
} 

// A (A4=440) 
// A# f(n)=2^(n/12) * r 
// B where n = number of semitones 
// C and r is the root frequency e.g. 440 
// C# 
// D frq -> MIDI note number 
// D# p = 69 + 12 x log2(f/440) 
// E 
// F  
// F# 
// G 
// G# 
// 
// MIDI Note ref: http://www.phys.unsw.edu.au/jw/notes.html 
// 
// MIDI Node numbers: 
// A3 57 
// A#3 58 
// B3 59 
// C4 60 <-- 
// C#4 61 
// D4 62 
// D#4 63 
// E4 64 
// F4 65 
// F#4 66 
// G4 67 
// G#4 68 
// A4 69 <-- 
// A#4 70 
// B4 71 
// C5 72 

float CalculateFrequencyFromNote(SInt32 semiTones, SInt32 octave) 
{ 
    semiTones += (12 * (octave-4)); 
    float root = 440.f; 
    float fn = powf(2.f, (float)semiTones/12.f) * root; 
    return fn; 
} 

float CalculateFrequencyFromMIDINote(SInt32 midiNoteNumber) 
{ 
    SInt32 semiTones = midiNoteNumber - 69; 
    return CalculateFrequencyFromNote(semiTones, 4); 
} 

//for (SInt32 midiNote=21; midiNote<=108; ++midiNote) 
//{ 
// printf("MIDI Note %d: %f Hz \n",(int)midiNote,CalculateFrequencyFromMIDINote(midiNote)); 
//} 

Cập nhật: Basic thông tin sử dụng

  1. khởi. Somehere gần đầu, tôi đang sử dụng initFromNib: trong mã của tôi

    m_bleepMachine = new BleepMachine; 
    m_bleepMachine->Initialise(); 
    m_bleepMachine->Start(); 
    
  2. Bây giờ phát lại âm thanh đang chạy, nhưng tạo ra sự im lặng.

  3. Trong code của bạn, gọi đây là khi bạn muốn thay đổi thế hệ tone

    m_bleepMachine->SetWave(ch, frq, vol); 
    
    • nơi ch là kênh (0 hoặc 1)
    • nơi frq là tần số để thiết lập theo đơn vị Hz
    • nơi vol là thể tích (0 = -INF db, 1 = -0db)
  4. Tại chương trình chấm dứt

    delete m_bleepMachine; 
    
+0

Cảm ơn Andy. Nhiều đánh giá cao. Tôi hy vọng tôi có thể tìm thấy một bộ công cụ sẽ xử lý một số công cụ cấp thấp hơn này cho tôi. Tôi đến từ SuperCollider, và thực sự chỉ có thể cắm các máy phát điện đơn vị vào nhau. Nhưng nhìn thấy một ví dụ về việc thực hiện cấp thấp hơn là rất tốt. Câu hỏi về newbie nhanh - là gì.mm mở rộng? Ngoài ra, nếu bạn cảm thấy muốn dán vào một ví dụ sử dụng lớp BleepMachine của bạn, tôi sẽ rất tuyệt vời. – morgancodes

+2

.mm extension = objective-C++ –

+0

Đã thêm thông tin sử dụng cơ bản để trả lời. –

0

PD has a version chạy trên iphone, sử dụng bởi RjDj. Nếu bạn đồng ý với việc sử dụng ứng dụng của người khác thay vì tự viết, bạn có thể làm một chút trong cảnh RjDj, và có một tập hợp các đối tượng cho phép bạn vá nó ra và kiểm tra nó trên một máy tính thông thường trên máy tính của bạn .

Tôi nên đề cập đến: PD là ngôn ngữ lập trình dữ liệu trực quan, có nghĩa là nó hoàn toàn có thể được sử dụng để phát triển các ứng dụng đồ họa - nhưng nếu bạn định làm bất cứ điều gì thú vị, tôi chắc chắn sẽ xem xét best practices for patching .

+0

Cảm ơn Justin, Tôi chắc chắn muốn có thể phát triển ứng dụng của riêng mình và không bị ràng buộc với RJDJ. Tôi biết SuperCollider là iPhone có thể, nhưng tôi nghĩ rằng giấy phép SC loại trừ việc sử dụng nó trong bất kỳ loại phần mềm thương mại nào, do đó, đó là một chút của một turn-off. – morgancodes

+0

Tôi thực sự là một người đóng góp cho supercollider, và nó không có hạn chế như vậy. Đó là GPL, có nghĩa là nếu bạn sửa đổi nguồn siêu dữ liệu, bạn cần phải chia sẻ những sửa đổi đó với bất kỳ ai nhận ứng dụng (giống như gcc là GPL, vì vậy bất kỳ sửa đổi nào bạn thực hiện đối với gcc để làm cho ứng dụng của bạn chạy cần được chia sẻ) , nhưng nếu mã của bạn đang sử dụng supercollider nhưng không sửa đổi supercollider thì không có hạn chế nào cả. Điều đó nói rằng, tôi khá chắc chắn bạn chỉ có thể sử dụng phiên bản iPhone của SC trên điện thoại jailbroken tại thời điểm này, đó là lý do tại sao tôi đã không đề nghị nó. –

+0

Nếu tôi nhớ chính xác, lý do mà sc không sẵn có thông qua cửa hàng ứng dụng là nó là một thông dịch viên chính thức. Bởi các tiêu chuẩn này RjDj cũng cần được loại trừ (PD là một nền tảng bất thường cho lập trình, nhưng nó là một môi trường lập trình), nhưng theo tôi không quan tâm. –

0

Lần trước tôi đã kiểm tra bạn không thể sử dụng AU tùy chỉnh trên iOS theo cách cho phép tất cả các ứng dụng đã cài đặt sử dụng nó (như trên MacOS X).Bạn có thể sử dụng lý thuyết AU tùy chỉnh từ bên trong ứng dụng iOS của bạn bằng cách tải nó từ gói ứng dụng và gọi hàm render của AU trực tiếp, nhưng sau đó bạn cũng có thể thêm mã trực tiếp vào ứng dụng của mình. Ngoài ra, tôi khá chắc chắn rằng tải và gọi mã mà ngồi trong một thư viện năng động sẽ đi ngược lại các chính sách AppStore.

Vì vậy, bạn sẽ phải thực hiện xử lý trong cuộc gọi lại từ xa IO của bạn hoặc sử dụng Apple AU được cài đặt sẵn, trong biểu đồ AUGraph.

3

Với báo trước rất lớn mà tôi chưa vượt qua tất cả tài liệu hoặc hoàn thành duyệt một số lớp/mã mẫu, có vẻ như những người tốt từ CCRMA ở Stanford có thể đã đặt một số bộ công cụ tốt đẹp với nhau . Không đảm bảo những điều này sẽ làm chính xác những gì bạn muốn, nhưng dựa trên những gì tôi biết về STK ban đầu, họ nên làm các trick. Tôi sắp bắt tay vào một ứng dụng synth âm thanh và tôi càng có thể sử dụng lại nhiều mã, thì càng tốt.

Liên kết/giới thiệu từ trang web của họ ...

MoMu: MoMu là một bộ công cụ phần mềm nhẹ để tạo nhạc cụ và kinh nghiệm trên thiết bị di động, và hiện hỗ trợ các nền tảng iPhone (iPhone, iPad, iPod Touch). MoMu cung cấp API cho âm thanh song công, gia tốc kế, vị trí, cảm ứng đa điểm, kết nối mạng (thông qua OpenSoundControl), đồ họa và các tiện ích theo thời gian thực. (Yada yada)

• và •

MoMu STK: Các MoMu phát hành của Bộ công cụ tổng hợp (STK, ban đầu bởi Perry R. Cook và Gary P. Scavone) là một phiên bản sửa đổi nhẹ của STK 4.4.2, và hiện đang hỗ trợ nền tảng iPhone (iPhone, iPad, iPod Touch).

+0

Cảm ơn Eric. STK là thứ tôi sử dụng cho ứng dụng của tôi, Thicket. Không chắc MoMu sẽ cung cấp cho bạn bao nhiêu tiền. Tôi tìm thấy nó khá dễ dàng để chỉ cần sử dụng một vài lớp STK và viết trực tiếp vào cuộc gọi lại RemoteIO. Không hoàn toàn chắc chắn về điểm của Momu. Hầu hết những thứ mà nó dường như cung cấp là khá dễ dàng để làm trực tiếp thông qua các lớp học Cocoa (với sự khởi đầu của OSC). – morgancodes

+1

Một mẹo nhỏ liên quan đến stk: Tôi đã tìm thấy tối qua rằng tôi nhận được khoảng 15% hiệu suất tăng nếu tôi thay đổi StkFloat để sử dụng phao thay vì tăng gấp đôi. Ngoài ra - điều này sẽ hiển nhiên đối với bất kỳ ai có trải nghiệm xử lý tín hiệu, nhưng không phải với tôi - nó có hiệu quả hơn nhiều khi có quá trình gọi phương thức một bộ đệm mẫu thay vì một mẫu đơn. – morgancodes

+0

+1 Cuộc gọi tốt Eric! Rất vui được xem tên của bạn trên Stack Overflow. -montag –

16

Vì bài đăng gốc của tôi gần một năm trước, tôi đã đi một chặng đường dài. Sau khi tìm kiếm khá đầy đủ, tôi đã đưa ra rất ít công cụ tổng hợp cấp cao phù hợp để phát triển iOS. Có rất nhiều giấy phép được cấp phép GPL, nhưng giấy phép GPL quá hạn chế để tôi cảm thấy thoải mái khi sử dụng nó. LibPD hoạt động tốt, và là những gì rjdj sử dụng, nhưng tôi thấy mình thực sự thất vọng bởi mô hình lập trình đồ họa. Động cơ dựa trên c của JSyn, csyn, là một lựa chọn, nhưng nó đòi hỏi phải cấp phép, và tôi thực sự được sử dụng để lập trình với các công cụ nguồn mở. Nó trông có giá trị một cái nhìn gần gũi mặc dù.

Cuối cùng, tôi đang sử dụng STK làm khung cơ bản của mình. STK là một công cụ rất thấp, và yêu cầu lập trình cấp bộ đệm mở rộng để làm việc. Điều này trái ngược với cấp độ cao hơn như PD hoặc SuperCollider, cho phép bạn chỉ cần cắm các bộ tạo đơn vị lại với nhau và không phải lo lắng về việc xử lý dữ liệu âm thanh thô.

Làm việc theo cách này với STK chắc chắn chậm hơn một chút so với công cụ cấp cao, nhưng tôi trở nên thoải mái với công cụ này. Đặc biệt bây giờ tôi đang trở nên thoải mái hơn với lập trình C/C++ nói chung.

Có một dự án mới đang được tiến hành để tạo kiểu chắp vá thêm vào Khung mở. Nó được gọi là Cleo tôi nghĩ, ngoài Đại học Vancouver. Nó chưa được phát hành, nhưng nó trông giống như một kết hợp rất tốt đẹp của kết nối vá kiểu máy phát điện đơn vị trong C++ thay vì yêu cầu sử dụng ngôn ngữ khác. Và nó được tích hợp chặt chẽ với Open Frameworks, có thể hấp dẫn hay không, tùy thuộc.

Vì vậy, để trả lời câu hỏi ban đầu của tôi, trước tiên bạn cần tìm hiểu cách ghi vào bộ đệm đầu ra.Dưới đây là một số mẫu mã tốt cho điều đó:

http://atastypixel.com/blog/using-remoteio-audio-unit/

Sau đó, bạn cần phải làm một số tổng hợp để tạo ra các dữ liệu âm thanh. Nếu bạn thích vá, tôi sẽ không ngần ngại giới thiệu libpd. Dường như nó hoạt động tốt và bạn có thể làm việc theo cách quen thuộc. Nếu bạn ghét bản vá đồ họa (như tôi), địa điểm bắt đầu tốt nhất của bạn bây giờ có lẽ là STK. Nếu STK và chương trình âm thanh cấp thấp có vẻ hơi hơn đầu bạn (giống như nó dành cho tôi), chỉ cần cuộn tay áo lên, đóng gói lều và thiết lập trên một đoạn đường dài lên đường cong học tập. Bạn sẽ là một lập trình viên tốt hơn cho nó cuối cùng.

Một chút lời khuyên khác mà tôi ước mình có thể đã đưa ra cho mình một năm trước: tham gia danh sách gửi thư của Apple Core Audio.

============== 2014 Sửa ===========

bây giờ tôi đang sử dụng (và tích cực đóng góp vào) quá trình tổng hợp âm thanh Tonic thư viện. Thật tuyệt vời, nếu tôi không tự nói như vậy.

+0

cảm ơn bạn đã chia sẻ trải nghiệm của mình – johnbakers

+0

Cảm ơn bạn đã cập nhật. –

+1

Có tài liệu nào về Tonic bên cạnh README không? Tôi muốn sử dụng nó cho một ứng dụng iOS. Cảm ơn vì công việc. – guardabrazo

1

Tôi vừa tham gia lập trình Đơn vị âm thanh cho iPhone để tạo ứng dụng giống như synth. Hướng dẫn Apple "Audio Đơn vị Hosting Hướng dẫn dành cho iOS" có vẻ như một tài liệu tham khảo tốt:

http://developer.apple.com/library/ios/#documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html#//apple_ref/doc/uid/TP40009492-CH3-SW11

hướng dẫn bao gồm các liên kết đến một dự án vài mẫu. Audio Mixer (MixerHost) và aurioTouch:

http://developer.apple.com/library/ios/samplecode/MixerHost/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010210

http://developer.apple.com/library/ios/samplecode/aurioTouch/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007770

+0

Ngoài ra, hướng dẫn này tuyệt vời: http://timbolstad.com/2010/03/14/core-audio-getting-started/ –

1

Tôi là một trong những người đóng góp khác để Tonic cùng với morgancodes. Để tranh luận CoreAudio trong một khuôn khổ cấp cao hơn, tôi không thể đưa ra lời khen ngợi đầy đủ cho The Amazing Audio Engine.

Cả hai chúng tôi đã sử dụng nó song song với Tonic trong một số dự án. Phải mất rất nhiều nỗi đau trong việc xử lý trực tiếp CoreAudio, cho phép bạn tập trung vào nội dung và tổng hợp thực tế thay vì lớp trừu tượng phần cứng.

1

Gần đây tôi đã sử dụng AudioKit

Đó là một wrapper tươi và được thiết kế tốt hơn CSound mà đã được khoảng cho các lứa tuổi

Tôi đã sử dụng thuốc bổ với openframeworks và tôi đã tìm kiếm bản thân mình thiếu lập trình trong nhanh chóng.

Mặc dù thuốc bổ và openframeworks đều công cụ mạnh mẽ,

tôi đã chọn để có được ở trên giường với nhanh chóng

+0

Cập nhật: Tôi đã quay lại các khung công tác mở cũ tốt sau khi tôi không nhận được loại âm thanh tôi muốn /// –

+0

Xin chào Paul, tôi thực sự là một trong những nhà phát triển của AudioKit ... xin lỗi bạn đã không nhận được loại âm thanh mà bạn đang theo dõi. Tôi rất muốn nghe thêm về những gì bạn đang theo dõi với AudioKit ... cảm thấy tự do để quay cho tôi một email tại nick (at) audiokit (chấm) io – narner

Các vấn đề liên quan