, где начать с синтеза звука на iPhone

17

Я бы хотел создать синтезатор для iPhone. Я понимаю, что для iPhone можно использовать собственные аудиоустройства. На первый взгляд это звучит многообещающе, так как доступно много и много доступных ресурсов программирования Audio Unit. Однако использование пользовательских аудиоустройств на iPhone кажется немного сложным (см .: Ссылка )

Это похоже на то, что нужно делать людям, но простой поиск Google для «синтеза звука iphone» не вызывает ничего похожего на хороший и простой учебник или рекомендованный набор инструментов.

/ р>

Итак, у кого-нибудь есть опыт синтеза звука на iPhone? Являются ли пользовательские аудиоустройства способным, или есть другой, более простой подход, который я должен рассмотреть?

    
задан morgancodes 14.01.2010 в 21:16
источник

8 ответов

21

Я также расследую это. Я думаю, что API AudioQueue, вероятно, способ пойти.

Вот, насколько я понял, все работает нормально.

Файл: 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 );

Файл: 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 ) );
//}

Обновление: основная информация об использовании

  1. Инициализировать. Где-то рядом с началом, я использую initFromNib: в моем коде

    m_bleepMachine = new BleepMachine;
    m_bleepMachine->Initialise();
    m_bleepMachine->Start();
    
  2. Теперь воспроизведение звука работает, но генерирует тишину.

  3. В своем коде вызовите это, если хотите изменить генерацию тона

    m_bleepMachine->SetWave( ch, frq, vol );
    
    • где ch - канал (0 или 1)
    • где frq - частота, заданная в Гц
    • где vol - это объем (0 = -Inf db, 1 = -0db)
  4. При завершении программы

    delete m_bleepMachine;
    
ответ дан Andy J Buchanan 14.01.2010 в 23:21
источник
16

Поскольку мой оригинальный пост почти год назад, я прошел долгий путь. После довольно исчерпывающего поиска я придумал очень мало инструментов синтеза на высоком уровне, подходящих для разработки iOS. Есть много лицензий GPL, но лицензия GPL слишком ограничительна для меня, чтобы чувствовать себя комфортно с ее помощью. LibPD отлично работает, и это то, что использует rjdj, но я действительно разочаровался в парадигме графического программирования. JSyn c-based engine, csyn, является вариантом, но он требует лицензирования, и я действительно привык к программированию с помощью инструментов с открытым исходным кодом. Это действительно стоит посмотреть внимательно.

В конце концов, я использую STK в качестве базовой структуры. STK является очень низкоуровневым инструментом и требует обширного программирования на уровне буфера для работы. Это контрастирует с чем-то более высоким уровнем, таким как PD или SuperCollider, который позволяет вам просто подключать генераторы модулей вместе, а не беспокоиться об обработке необработанных аудиоданных.

Работа с STK, конечно, немного медленнее, чем при использовании инструмента высокого уровня, но мне становится комфортно. Особенно сейчас, когда мне становится все более комфортно с программированием на C / C ++.

В настоящее время существует новый проект по созданию дополнения в Open Framework. Это называется Клео, я думаю, из Университета Ванкувера. Он еще не выпущен, но это похоже на очень приятное сочетание патч-стиля подключения генераторов блоков в C ++, вместо того, чтобы требовать использования другого языка. И он тесно интегрирован с Open Frameworks, который может быть привлекательным или нет, в зависимости.

Итак, чтобы ответить на мой первоначальный вопрос, сначала вам нужно научиться писать в выходной буфер. Вот несколько хороших примеров кода:

Ссылка

Затем вам нужно сделать синтез для генерации аудиоданных. Если вам нравится исправление, я без колебаний рекомендую libpd . Кажется, он отлично работает, и вы можете работать так, как вы привыкли. Если вы ненавидите графическое исправление (например, я), вашим лучшим стартовым местом сейчас является STK . Если STK и низкоуровневое аудиопрограммирование кажутся немного над вашей головой (как это было для меня), просто сверните свои рукава, упакуйте палатку и немного настройтесь на кривую обучения. В конце концов, вы будете намного лучше программистом.

Еще один совет, который я хотел бы дать себе год назад: присоединитесь к списку рассылки Core Audio от Apple.

============== 2014 Редактировать ===========

Теперь я использую (и активно участвую) библиотеку синтезаторов Tonic . Это здорово, если я так не говорю.

    
ответ дан morgancodes 24.02.2011 в 17:58
источник
3

С огромным предостережением, что мне еще предстоит пройти всю документацию или закончить просмотр некоторых классов / пример кода, похоже, что прекрасные люди из CCRMA в Стэнфорде, возможно, собрали несколько хороших наборов инструментов для нашего удовольствия от взлома звука. Нет гарантий, что они будут делать именно то, что вы хотите, но основываясь на том, что я знаю об оригинальной STK, они должны сделать трюк. Я собираюсь начать собственное аудио-синтезационное приложение, и чем больше кода я могу повторно использовать, тем лучше.

Ссылки / описания со своего сайта ...

MoMu : MoMu - это легкий инструментарий для создания музыкальных инструментов и опыта на мобильном устройстве, и в настоящее время он поддерживает Платформа iPhone (iPhone, iPad, iPod Touches). MoMu предоставляет API для полнодуплексного звука в реальном времени, акселерометра, местоположения, мультитач, сетей (через OpenSoundControl), графики и утилит. (yada yada)

• и

MoMu STK : выпуск MoMu инструментария синтеза (STK, первоначально Perry R. Cook и Gary P. Scavone ) представляет собой слегка модифицированную версию STK 4.4.2 и в настоящее время поддерживает платформу iPhone (iPhone, iPad, iPod Touch).

    
ответ дан Eric Humphrey 23.02.2011 в 06:32
источник
1

Я просто вхожу в программу Audio Unit для iPhone, чтобы создать подобное синтезатору приложение. Руководство Apple «Руководство для хостинга аудиоустройств для iOS» похоже на хорошую ссылку:

Ссылка

Руководство содержит ссылки на несколько примеров проектов. Audio Mixer (MixerHost) и aurioTouch:

Ссылка

Ссылка     

ответ дан Chris Livdahl 26.02.2011 в 23:12
источник
1

Я один из других участников Тоник вместе с морганкодами. Для того, чтобы прервать CoreAudio в рамках более высокого уровня, я не могу дать достаточно похвалы Amazing Audio Engine .

Мы оба использовали его в тандеме с Tonic в ряде проектов. Большую часть этой проблемы занимает работа с CoreAudio напрямую, что позволяет сосредоточиться на фактическом содержании и синтезе вместо уровня абстракции оборудования.

    
ответ дан roperklacks 05.03.2014 в 21:39
источник
1

В последнее время я использую AudioKit

Это свежая и хорошо продуманная оболочка поверх CSound , которая существует уже давно [/ p>

Я использовал тоник с openframeworks, и я быстро терял программирование.

Хотя тонические и openframework являются мощными инструментами,

Я решил быстро встать с постели

    
ответ дан Paul Wand 03.04.2015 в 05:50
источник
0

PD имеет версию , которая работает на iphone, используется RjDj . Если у вас все в порядке с использованием чужого приложения, а не на собственном, вы можете сделать совсем немного в сцене RjDj, и есть набор объектов, которые позволяют вам его исправлять и тестировать на обычном PD на вашем собственном компьютере .

Я должен упомянуть: PD - это язык программирования визуального потока данных, т. е. он полностью завершен и может использоваться для разработки графических приложений, но если вы собираетесь делать что-нибудь интересное, я обязательно посмотрю на рекомендации по исправлению .

    
ответ дан Justin Smith 20.01.2010 в 15:27
источник
0

В прошлый раз, когда я проверил, вы не можете использовать пользовательские АС в iOS таким образом, чтобы все установленные приложения могли использовать его (например, на MacOS X).

Теоретически вы можете использовать пользовательский AU из своего приложения iOS, загрузив его из пакета приложения и напрямую вызовите функцию визуализации AU, но затем вы можете добавить код непосредственно в свое приложение. Кроме того, я уверен, что код загрузки и вызова, который находится в динамической библиотеке, будет противоречить правилам AppStore.

Таким образом, вам придется либо выполнить обработку в удаленном обратном вызове ввода-вывода, либо использовать предустановленные Apple AU, в AUGraph.

    
ответ дан tahome 18.02.2011 в 13:40
источник