mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-06 14:52:34 +02:00
------------------------------------------------------------------------ r1966 | philburk | 2016-02-15 11:06:54 -0600 (Mon, 15 Feb 2016) | 3 lines Update docs for building on Linux ------------------------------------------------------------------------ r1965 | philburk | 2016-01-04 11:22:56 -0600 (Mon, 04 Jan 2016) | 6 lines Files needed for compiling on El Capitan. These files were autogenerated using autoreconf based on changes from the previous commit. ------------------------------------------------------------------------ r1964 | philburk | 2016-01-04 11:22:00 -0600 (Mon, 04 Jan 2016) | 9 lines Add support for 10.11 SDK so PortAudio can compile on Mac. Also allow deprecated APIs until we can remove them. Note that this commit only includes the source files that were changed. The next commit will include the files that are generated by autoreconf. So look at this commit to see what really changed. ------------------------------------------------------------------------ r1963 | dmitrykos | 2015-10-21 09:25:07 -0500 (Wed, 21 Oct 2015) | 1 line wasapi: correction to the previous commit ------------------------------------------------------------------------ r1962 | dmitrykos | 2015-10-20 11:02:39 -0500 (Tue, 20 Oct 2015) | 1 line wasapi: workaround to avoid PaWasapi_Initialize() failure when enumerating devices and device fails to report min/default periods with IAudioClient::GetDevicePeriod(): ignore error and assign WASAPI common known period values for min/default ------------------------------------------------------------------------ r1961 | dmitrykos | 2015-10-19 06:03:31 -0500 (Mon, 19 Oct 2015) | 1 line wasapi: support for setting the WASAPI's AudioClientProperties options via PaWasapiStreamInfo struct (__IAudioClient2_INTERFACE_DEFINED__ must be defined by Windows SDK (for Windows 8 and up) to have effect from these new options, otherwise it will be noop) ------------------------------------------------------------------------ r1960 | rbencina | 2015-09-06 08:17:05 -0500 (Sun, 06 Sep 2015) | 1 line update doxygen main page to reference developer guidelines and implementation style guidelines on the Wiki ------------------------------------------------------------------------ r1959 | rbencina | 2015-09-06 07:43:06 -0500 (Sun, 06 Sep 2015) | 1 line checkfiledocs.py: blacklist mingw-include from doc check ------------------------------------------------------------------------ r1958 | rbencina | 2015-09-06 07:37:38 -0500 (Sun, 06 Sep 2015) | 1 line checkfiledocs.py: only check source code under src and include ------------------------------------------------------------------------ r1957 | rbencina | 2015-09-06 05:55:01 -0500 (Sun, 06 Sep 2015) | 1 line Issue #234 fixed typo in configure and configure.in affecting mingw builds. Rename: pa_win_wdmks_util.o --> pa_win_wdmks_utils.o ------------------------------------------------------------------------ r1956 | rbencina | 2015-09-06 05:26:16 -0500 (Sun, 06 Sep 2015) | 1 line removed bindings/java from doxygen generated documentation ------------------------------------------------------------------------ r1954 | gineera | 2015-04-19 13:48:38 -0500 (Sun, 19 Apr 2015) | 1 line Pa_process: Fix output channel adaption by not skipping the conversion when the host and user number-of-channels are not equal. Reported by Leif Asbrink when using an envy24 soundcard on Linux at it's native Int32 format with 1 or 2 channels. Corresponding fix for input already in r1913. ------------------------------------------------------------------------ r1953 | philburk | 2015-04-09 23:00:09 -0500 (Thu, 09 Apr 2015) | 4 lines Add Pa_GetVersionInfo() Add scripts to update pa_svnrevision.h ------------------------------------------------------------------------ r1952 | philburk | 2015-03-12 11:23:11 -0500 (Thu, 12 Mar 2015) | 3 lines Test adding a second file with SVN keywords. ------------------------------------------------------------------------ r1951 | philburk | 2015-03-12 11:21:04 -0500 (Thu, 12 Mar 2015) | 3 lines Used propset to enable SVN keywords on test file. ------------------------------------------------------------------------ r1950 | philburk | 2015-03-12 11:16:44 -0500 (Thu, 12 Mar 2015) | 3 lines Experiment with the SVN version keywords. ------------------------------------------------------------------------ r1949 | philburk | 2015-03-03 11:28:15 -0600 (Tue, 03 Mar 2015) | 4 lines Add version numbering with major.minor.subminor format. Bump version to 19.5.0 ------------------------------------------------------------------------ r1948 | rbencina | 2015-01-21 00:52:11 -0600 (Wed, 21 Jan 2015) | 1 line pa/wmme: avoid potential (but highly unlikely) overflow in buffer size in call to WideCharToMultiByte if a device name length exceeds INT_MAX. should also fix compiler warning about size_t to int assignment. ------------------------------------------------------------------------ r1947 | rbencina | 2015-01-21 00:32:29 -0600 (Wed, 21 Jan 2015) | 1 line removed declaration of unused variable ------------------------------------------------------------------------ r1946 | rbencina | 2015-01-21 00:30:53 -0600 (Wed, 21 Jan 2015) | 1 line fixed compiler warning: conversion from 'double' to 'float', possible loss of data ------------------------------------------------------------------------ r1945 | rbencina | 2015-01-21 00:24:32 -0600 (Wed, 21 Jan 2015) | 1 line disabled deprecated API warning for GetVersionEx in dsound, wmme and wdmks host APIs. ------------------------------------------------------------------------ r1944 | rbencina | 2015-01-20 23:35:39 -0600 (Tue, 20 Jan 2015) | 1 line Removed MSVC dependence on ksguid.lib from src/os/win/pa_win_wdmks_utils.c (ksguid.lib is no longer in Platform SDK 8.0). Always use static instances of GUIDs like the GCC builds do. Removed ksguid.lib from MSVC project file. The following symbols are no longer used: PA_WDMKS_NO_KSGUID_LIB, PAWIN_WDMKS_NO_KSGUID_LIB; removed them from CMakeLists.txt and the MSVC project file. ------------------------------------------------------------------------ r1943 | rbencina | 2015-01-19 23:10:17 -0600 (Mon, 19 Jan 2015) | 1 line cpp binding: make operator == and != const. thanks to Riot for this patch. ticket #230 ------------------------------------------------------------------------ r1934 | rbencina | 2014-11-04 19:03:57 -0600 (Tue, 04 Nov 2014) | 1 line PA/CoreAudio: fixed copy-pasto: last host error was being associated with paInDevelopment host api not paCoreAudio ------------------------------------------------------------------------ r1933 | robiwan | 2014-08-28 00:59:40 -0500 (Thu, 28 Aug 2014) | 1 line Added more debugging info in wdmks/pa_win_wdmks.c ------------------------------------------------------------------------ r1932 | robiwan | 2014-08-15 01:35:32 -0500 (Fri, 15 Aug 2014) | 1 line Committing Lelands patch for WDM-KS, should take care of some of the problems referenced here http://music.columbia.edu/pipermail/portaudio/2014-August/016246.html ------------------------------------------------------------------------ r1930 | bejayoharen | 2014-06-28 11:28:17 -0500 (Sat, 28 Jun 2014) | 1 line olivier's device name patch for os x ------------------------------------------------------------------------ r1928 | rbencina | 2014-04-11 00:46:40 -0500 (Fri, 11 Apr 2014) | 1 line pa_win_ds.c converted tabs to spaces (only a small amount of code was incorrectly using tabs). ------------------------------------------------------------------------ r1927 | rbencina | 2014-04-11 00:44:50 -0500 (Fri, 11 Apr 2014) | 1 line DirectSound: correctly output device names as UTF-8 when compiled with UNICODE defined. Note that this patch may not be correct if UNICODE is not defined. Patch from Tobias Erichsen. See ticket #224 for details. ------------------------------------------------------------------------ r1926 | rbencina | 2014-04-11 00:31:13 -0500 (Fri, 11 Apr 2014) | 1 line pa_win_wmme.c: converted tabs to spaces (just a few sections) ------------------------------------------------------------------------ r1925 | rbencina | 2014-04-11 00:25:48 -0500 (Fri, 11 Apr 2014) | 1 line WMME: correctly convert device names to UTF-8, see ticket #224. Thanks to Tobias Erichsen for the patch. ------------------------------------------------------------------------ r1924 | robiwan | 2014-04-09 09:27:21 -0500 (Wed, 09 Apr 2014) | 4 lines CMake: Added PA_WDMKS_NO_KSGUID_LIB to WDMKS and solution folders WDMKS: Support for default device (see caveat for input devices though in ScanDeviceInfos) Added possibility to set channel mask ------------------------------------------------------------------------ r1920 | bejayoharen | 2014-02-05 12:43:31 -0600 (Wed, 05 Feb 2014) | 1 line Clarified safety of operations in callback (doc) ------------------------------------------------------------------------ r1919 | rbencina | 2014-01-30 05:46:42 -0600 (Thu, 30 Jan 2014) | 1 line updated Windows+ASIO build tutorial: Building Portaudio for Windows with ASIO support using MSVC. The tutorial was out of date and contained a number of errors that broke the build process. ------------------------------------------------------------------------ r1918 | rbencina | 2014-01-30 04:12:02 -0600 (Thu, 30 Jan 2014) | 1 line tweaked Windows build tutorial: Building PortAudio for Windows using Microsoft Visual Studio - various improvements to clarity of text. Fixed broken ASIO SDK URL. ------------------------------------------------------------------------ r1916 | philburk | 2014-01-16 21:45:15 -0600 (Thu, 16 Jan 2014) | 6 lines Add stub to pa_trace.c to eliminate warning. "make install" was causing a warning if PA_TRACE_REALTIME_EVENTS was not defined. ------------------------------------------------------------------------ r1915 | philburk | 2014-01-16 11:51:26 -0600 (Thu, 16 Jan 2014) | 5 lines [tests] Add EOL to patest_mono.c Build was failing on Mac because of the missing end-of-line. ------------------------------------------------------------------------ r1914 | philburk | 2014-01-16 11:49:33 -0600 (Thu, 16 Jan 2014) | 6 lines [macosx] Add support for SDK 10.8 and 10.9. This was required to fix a broken build caused by not having SDK 10.7 or earlier. ------------------------------------------------------------------------ r1913 | gineera | 2013-11-18 05:42:27 -0600 (Mon, 18 Nov 2013) | 1 line Pa_process: Fix input channel adaption by not skipping the input conversion when the host and user number-of-channels are not equal. The bug manifested when recording mono from a stereo-only device (eg with Alsa hw: devices on some sound-cards), and was reported by the Mixxx team -see https://bugs.launchpad.net/mixxx/+bug/900364. (In the long-term, some refactoring may be preferable.) Also fixed copy-paste typos in comments. ------------------------------------------------------------------------ r1912 | gineera | 2013-11-15 06:27:07 -0600 (Fri, 15 Nov 2013) | 1 line Jack: Add a port-type filter expression to calls to jack_get_ports() so that only audio ports are listed (removing eg Midi etc) as reported by 'sqweek'. ------------------------------------------------------------------------ r1911 | gineera | 2013-10-17 07:44:09 -0500 (Thu, 17 Oct 2013) | 1 line Alsa: Revise the use of the environment variable 'PA_ALSA_PLUGHW' so it correctly influences the Device-List and the capabilities reported. Also rename a mis-leading variable and add one additional DEBUG line.
534 lines
18 KiB
C
534 lines
18 KiB
C
/** @file paex_ocean_shore.c
|
|
@ingroup examples_src
|
|
@brief Generate Pink Noise using Gardner method, and make "waves". Provides an example of how to
|
|
post stuff to/from the audio callback using lock-free FIFOs implemented by the PA ringbuffer.
|
|
|
|
Optimization suggested by James McCartney uses a tree
|
|
to select which random value to replace.
|
|
<pre>
|
|
x x x x x x x x x x x x x x x x
|
|
x x x x x x x x
|
|
x x x x
|
|
x x
|
|
x
|
|
</pre>
|
|
Tree is generated by counting trailing zeros in an increasing index.
|
|
When the index is zero, no random number is selected.
|
|
|
|
@author Phil Burk http://www.softsynth.com
|
|
Robert Bielik
|
|
*/
|
|
/*
|
|
* $Id: paex_ocean_shore.c 1946 2015-01-21 06:30:53Z rbencina $
|
|
*
|
|
* This program uses the PortAudio Portable Audio Library.
|
|
* For more information see: http://www.portaudio.com
|
|
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* The text above constitutes the entire PortAudio license; however,
|
|
* the PortAudio community also makes the following non-binding requests:
|
|
*
|
|
* Any person wishing to distribute modifications to the Software is
|
|
* requested to send the modifications to the original developer so that
|
|
* they can be incorporated into the canonical version. It is also
|
|
* requested that these non-binding requests be included along with the
|
|
* license above.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
|
|
#include "portaudio.h"
|
|
#include "pa_ringbuffer.h"
|
|
#include "pa_util.h"
|
|
|
|
#define PINK_MAX_RANDOM_ROWS (30)
|
|
#define PINK_RANDOM_BITS (24)
|
|
#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
|
|
|
|
typedef struct
|
|
{
|
|
long pink_Rows[PINK_MAX_RANDOM_ROWS];
|
|
long pink_RunningSum; /* Used to optimize summing of generators. */
|
|
int pink_Index; /* Incremented each sample. */
|
|
int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
|
|
float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
|
|
}
|
|
PinkNoise;
|
|
|
|
typedef struct
|
|
{
|
|
float bq_b0;
|
|
float bq_b1;
|
|
float bq_b2;
|
|
float bq_a1;
|
|
float bq_a2;
|
|
} BiQuad;
|
|
|
|
typedef enum
|
|
{
|
|
State_kAttack,
|
|
State_kPreDecay,
|
|
State_kDecay,
|
|
State_kCnt,
|
|
} EnvState;
|
|
|
|
typedef struct
|
|
{
|
|
PinkNoise wave_left;
|
|
PinkNoise wave_right;
|
|
|
|
BiQuad wave_bq_coeffs;
|
|
float wave_bq_left[2];
|
|
float wave_bq_right[2];
|
|
|
|
EnvState wave_envelope_state;
|
|
float wave_envelope_level;
|
|
float wave_envelope_max_level;
|
|
float wave_pan_left;
|
|
float wave_pan_right;
|
|
float wave_attack_incr;
|
|
float wave_decay_incr;
|
|
|
|
} OceanWave;
|
|
|
|
/* Prototypes */
|
|
static unsigned long GenerateRandomNumber( void );
|
|
void InitializePinkNoise( PinkNoise *pink, int numRows );
|
|
float GeneratePinkNoise( PinkNoise *pink );
|
|
unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames);
|
|
|
|
/************************************************************/
|
|
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
|
|
static unsigned long GenerateRandomNumber( void )
|
|
{
|
|
/* Change this seed for different random sequences. */
|
|
static unsigned long randSeed = 22222;
|
|
randSeed = (randSeed * 196314165) + 907633515;
|
|
return randSeed;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* Setup PinkNoise structure for N rows of generators. */
|
|
void InitializePinkNoise( PinkNoise *pink, int numRows )
|
|
{
|
|
int i;
|
|
long pmax;
|
|
pink->pink_Index = 0;
|
|
pink->pink_IndexMask = (1<<numRows) - 1;
|
|
/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
|
|
pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
|
|
pink->pink_Scalar = 1.0f / pmax;
|
|
/* Initialize rows. */
|
|
for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
|
|
pink->pink_RunningSum = 0;
|
|
}
|
|
|
|
/* Generate Pink noise values between -1.0 and +1.0 */
|
|
float GeneratePinkNoise( PinkNoise *pink )
|
|
{
|
|
long newRandom;
|
|
long sum;
|
|
float output;
|
|
/* Increment and mask index. */
|
|
pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
|
|
/* If index is zero, don't update any random values. */
|
|
if( pink->pink_Index != 0 )
|
|
{
|
|
/* Determine how many trailing zeros in PinkIndex. */
|
|
/* This algorithm will hang if n==0 so test first. */
|
|
int numZeros = 0;
|
|
int n = pink->pink_Index;
|
|
while( (n & 1) == 0 )
|
|
{
|
|
n = n >> 1;
|
|
numZeros++;
|
|
}
|
|
/* Replace the indexed ROWS random value.
|
|
* Subtract and add back to RunningSum instead of adding all the random
|
|
* values together. Only one changes each time.
|
|
*/
|
|
pink->pink_RunningSum -= pink->pink_Rows[numZeros];
|
|
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
|
|
pink->pink_RunningSum += newRandom;
|
|
pink->pink_Rows[numZeros] = newRandom;
|
|
}
|
|
|
|
/* Add extra white noise value. */
|
|
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
|
|
sum = pink->pink_RunningSum + newRandom;
|
|
/* Scale to range of -1.0 to 0.9999. */
|
|
output = pink->pink_Scalar * sum;
|
|
return output;
|
|
}
|
|
|
|
float ProcessBiquad(const BiQuad* coeffs, float* memory, float input)
|
|
{
|
|
float w = input - coeffs->bq_a1 * memory[0] - coeffs->bq_a2 * memory[1];
|
|
float out = coeffs->bq_b1 * memory[0] + coeffs->bq_b2 * memory[1] + coeffs->bq_b0 * w;
|
|
memory[1] = memory[0];
|
|
memory[0] = w;
|
|
return out;
|
|
}
|
|
|
|
static const float one_over_2Q_LP = 0.3f;
|
|
static const float one_over_2Q_HP = 1.0f;
|
|
|
|
unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames )
|
|
{
|
|
unsigned retval=0,i;
|
|
float targetLevel, levelIncr, currentLevel;
|
|
switch (wave->wave_envelope_state)
|
|
{
|
|
case State_kAttack:
|
|
targetLevel = noOfFrames * wave->wave_attack_incr + wave->wave_envelope_level;
|
|
if (targetLevel >= wave->wave_envelope_max_level)
|
|
{
|
|
/* Go to decay state */
|
|
wave->wave_envelope_state = State_kPreDecay;
|
|
targetLevel = wave->wave_envelope_max_level;
|
|
}
|
|
/* Calculate lowpass biquad coeffs
|
|
|
|
alpha = sin(w0)/(2*Q)
|
|
|
|
b0 = (1 - cos(w0))/2
|
|
b1 = 1 - cos(w0)
|
|
b2 = (1 - cos(w0))/2
|
|
a0 = 1 + alpha
|
|
a1 = -2*cos(w0)
|
|
a2 = 1 - alpha
|
|
|
|
w0 = [0 - pi[
|
|
*/
|
|
{
|
|
const float w0 = 3.141592654f * targetLevel / wave->wave_envelope_max_level;
|
|
const float alpha = sinf(w0) * one_over_2Q_LP;
|
|
const float cosw0 = cosf(w0);
|
|
const float a0_fact = 1.0f / (1.0f + alpha);
|
|
wave->wave_bq_coeffs.bq_b1 = (1.0f - cosw0) * a0_fact;
|
|
wave->wave_bq_coeffs.bq_b0 = wave->wave_bq_coeffs.bq_b1 * 0.5f;
|
|
wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
|
|
wave->wave_bq_coeffs.bq_a2 = (1.0f - alpha) * a0_fact;
|
|
wave->wave_bq_coeffs.bq_a1 = -2.0f * cosw0 * a0_fact;
|
|
}
|
|
break;
|
|
|
|
case State_kPreDecay:
|
|
/* Reset biquad state */
|
|
memset(wave->wave_bq_left, 0, 2 * sizeof(float));
|
|
memset(wave->wave_bq_right, 0, 2 * sizeof(float));
|
|
wave->wave_envelope_state = State_kDecay;
|
|
|
|
/* Deliberate fall-through */
|
|
|
|
case State_kDecay:
|
|
targetLevel = noOfFrames * wave->wave_decay_incr + wave->wave_envelope_level;
|
|
if (targetLevel < 0.001f)
|
|
{
|
|
/* < -60 dB, we're done */
|
|
wave->wave_envelope_state = 3;
|
|
retval = 1;
|
|
}
|
|
/* Calculate highpass biquad coeffs
|
|
|
|
alpha = sin(w0)/(2*Q)
|
|
|
|
b0 = (1 + cos(w0))/2
|
|
b1 = -(1 + cos(w0))
|
|
b2 = (1 + cos(w0))/2
|
|
a0 = 1 + alpha
|
|
a1 = -2*cos(w0)
|
|
a2 = 1 - alpha
|
|
|
|
w0 = [0 - pi/2[
|
|
*/
|
|
{
|
|
const float v = targetLevel / wave->wave_envelope_max_level;
|
|
const float w0 = 1.5707963f * (1.0f - (v*v));
|
|
const float alpha = sinf(w0) * one_over_2Q_HP;
|
|
const float cosw0 = cosf(w0);
|
|
const float a0_fact = 1.0f / (1.0f + alpha);
|
|
wave->wave_bq_coeffs.bq_b1 = (float)(- (1 + cosw0) * a0_fact);
|
|
wave->wave_bq_coeffs.bq_b0 = -wave->wave_bq_coeffs.bq_b1 * 0.5f;
|
|
wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
|
|
wave->wave_bq_coeffs.bq_a2 = (float)((1.0 - alpha) * a0_fact);
|
|
wave->wave_bq_coeffs.bq_a1 = (float)(-2.0 * cosw0 * a0_fact);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
currentLevel = wave->wave_envelope_level;
|
|
wave->wave_envelope_level = targetLevel;
|
|
levelIncr = (targetLevel - currentLevel) / noOfFrames;
|
|
|
|
for (i = 0; i < noOfFrames; ++i, currentLevel += levelIncr)
|
|
{
|
|
(*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_left, (GeneratePinkNoise(&wave->wave_left))) * currentLevel * wave->wave_pan_left;
|
|
(*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_right, (GeneratePinkNoise(&wave->wave_right))) * currentLevel * wave->wave_pan_right;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*******************************************************************/
|
|
|
|
/* Context for callback routine. */
|
|
typedef struct
|
|
{
|
|
OceanWave* waves[16]; /* Maximum 16 waves */
|
|
unsigned noOfActiveWaves;
|
|
|
|
/* Ring buffer (FIFO) for "communicating" towards audio callback */
|
|
PaUtilRingBuffer rBufToRT;
|
|
void* rBufToRTData;
|
|
|
|
/* Ring buffer (FIFO) for "communicating" from audio callback */
|
|
PaUtilRingBuffer rBufFromRT;
|
|
void* rBufFromRTData;
|
|
}
|
|
paTestData;
|
|
|
|
/* This routine will be called by the PortAudio engine when audio is needed.
|
|
** It may called at interrupt level on some machines so don't do anything
|
|
** that could mess up the system like calling malloc() or free().
|
|
*/
|
|
static int patestCallback(const void* inputBuffer,
|
|
void* outputBuffer,
|
|
unsigned long framesPerBuffer,
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
PaStreamCallbackFlags statusFlags,
|
|
void* userData)
|
|
{
|
|
int i;
|
|
paTestData *data = (paTestData*)userData;
|
|
float *out = (float*)outputBuffer;
|
|
(void) inputBuffer; /* Prevent "unused variable" warnings. */
|
|
|
|
/* Reset output data first */
|
|
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
/* Consume the input queue */
|
|
if (data->waves[i] == 0 && PaUtil_GetRingBufferReadAvailable(&data->rBufToRT))
|
|
{
|
|
OceanWave* ptr = 0;
|
|
PaUtil_ReadRingBuffer(&data->rBufToRT, &ptr, 1);
|
|
data->waves[i] = ptr;
|
|
}
|
|
|
|
if (data->waves[i] != 0)
|
|
{
|
|
if (GenerateWave(data->waves[i], out, framesPerBuffer))
|
|
{
|
|
/* If wave is "done", post it back to the main thread for deletion */
|
|
PaUtil_WriteRingBuffer(&data->rBufFromRT, &data->waves[i], 1);
|
|
data->waves[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
return paContinue;
|
|
}
|
|
|
|
#define NEW_ROW_SIZE (12 + (8*rand())/RAND_MAX)
|
|
|
|
OceanWave* InitializeWave(double SR, float attackInSeconds, float maxLevel, float positionLeftRight)
|
|
{
|
|
OceanWave* wave = NULL;
|
|
static unsigned lastNoOfRows = 12;
|
|
unsigned newNoOfRows;
|
|
|
|
wave = (OceanWave*)PaUtil_AllocateMemory(sizeof(OceanWave));
|
|
if (wave != NULL)
|
|
{
|
|
InitializePinkNoise(&wave->wave_left, lastNoOfRows);
|
|
while ((newNoOfRows = NEW_ROW_SIZE) == lastNoOfRows);
|
|
InitializePinkNoise(&wave->wave_right, newNoOfRows);
|
|
lastNoOfRows = newNoOfRows;
|
|
|
|
wave->wave_envelope_state = State_kAttack;
|
|
wave->wave_envelope_level = 0.f;
|
|
wave->wave_envelope_max_level = maxLevel;
|
|
wave->wave_attack_incr = wave->wave_envelope_max_level / (attackInSeconds * (float)SR);
|
|
wave->wave_decay_incr = - wave->wave_envelope_max_level / (attackInSeconds * 4 * (float)SR);
|
|
|
|
wave->wave_pan_left = sqrtf(1.0f - positionLeftRight);
|
|
wave->wave_pan_right = sqrtf(positionLeftRight);
|
|
}
|
|
return wave;
|
|
}
|
|
|
|
static float GenerateFloatRandom(float minValue, float maxValue)
|
|
{
|
|
return minValue + ((maxValue - minValue) * rand()) / RAND_MAX;
|
|
}
|
|
|
|
/*******************************************************************/
|
|
int main(void);
|
|
int main(void)
|
|
{
|
|
PaStream* stream;
|
|
PaError err;
|
|
paTestData data = {0};
|
|
PaStreamParameters outputParameters;
|
|
double tstamp;
|
|
double tstart;
|
|
double tdelta = 0;
|
|
static const double SR = 44100.0;
|
|
static const int FPB = 128; /* Frames per buffer: 2.9 ms buffers. */
|
|
|
|
/* Initialize communication buffers (queues) */
|
|
data.rBufToRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
|
|
if (data.rBufToRTData == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
PaUtil_InitializeRingBuffer(&data.rBufToRT, sizeof(OceanWave*), 256, data.rBufToRTData);
|
|
|
|
data.rBufFromRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
|
|
if (data.rBufFromRTData == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
PaUtil_InitializeRingBuffer(&data.rBufFromRT, sizeof(OceanWave*), 256, data.rBufFromRTData);
|
|
|
|
err = Pa_Initialize();
|
|
if( err != paNoError ) goto error;
|
|
|
|
/* Open a stereo PortAudio stream so we can hear the result. */
|
|
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
|
|
if (outputParameters.device == paNoDevice) {
|
|
fprintf(stderr,"Error: No default output device.\n");
|
|
goto error;
|
|
}
|
|
outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
|
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
|
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
|
|
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
|
|
err = Pa_OpenStream(&stream,
|
|
NULL, /* No input. */
|
|
&outputParameters,
|
|
SR, /* Sample rate. */
|
|
FPB, /* Frames per buffer. */
|
|
paDitherOff, /* Clip but don't dither */
|
|
patestCallback,
|
|
&data);
|
|
if( err != paNoError ) goto error;
|
|
|
|
err = Pa_StartStream( stream );
|
|
if( err != paNoError ) goto error;
|
|
|
|
printf("Stereo \"ocean waves\" for one minute...\n");
|
|
|
|
tstart = PaUtil_GetTime();
|
|
tstamp = tstart;
|
|
srand( (unsigned)time(NULL) );
|
|
|
|
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
|
|
{
|
|
const double tcurrent = PaUtil_GetTime();
|
|
|
|
/* Delete "waves" that the callback is finished with */
|
|
while (PaUtil_GetRingBufferReadAvailable(&data.rBufFromRT) > 0)
|
|
{
|
|
OceanWave* ptr = 0;
|
|
PaUtil_ReadRingBuffer(&data.rBufFromRT, &ptr, 1);
|
|
if (ptr != 0)
|
|
{
|
|
printf("Wave is deleted...\n");
|
|
PaUtil_FreeMemory(ptr);
|
|
--data.noOfActiveWaves;
|
|
}
|
|
}
|
|
|
|
if (tcurrent - tstart < 60.0) /* Only start new "waves" during one minute */
|
|
{
|
|
if (tcurrent >= tstamp)
|
|
{
|
|
double tdelta = GenerateFloatRandom(1.0f, 4.0f);
|
|
tstamp += tdelta;
|
|
|
|
if (data.noOfActiveWaves<16)
|
|
{
|
|
const float attackTime = GenerateFloatRandom(2.0f, 6.0f);
|
|
const float level = GenerateFloatRandom(0.1f, 1.0f);
|
|
const float pos = GenerateFloatRandom(0.0f, 1.0f);
|
|
OceanWave* p = InitializeWave(SR, attackTime, level, pos);
|
|
if (p != NULL)
|
|
{
|
|
/* Post wave to audio callback */
|
|
PaUtil_WriteRingBuffer(&data.rBufToRT, &p, 1);
|
|
++data.noOfActiveWaves;
|
|
|
|
printf("Starting wave at level = %.2f, attack = %.2lf, pos = %.2lf\n", level, attackTime, pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (data.noOfActiveWaves == 0)
|
|
{
|
|
printf("All waves finished!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
Pa_Sleep(100);
|
|
}
|
|
if( err < 0 ) goto error;
|
|
|
|
err = Pa_CloseStream( stream );
|
|
if( err != paNoError ) goto error;
|
|
|
|
if (data.rBufToRTData)
|
|
{
|
|
PaUtil_FreeMemory(data.rBufToRTData);
|
|
}
|
|
if (data.rBufFromRTData)
|
|
{
|
|
PaUtil_FreeMemory(data.rBufFromRTData);
|
|
}
|
|
|
|
Pa_Sleep(1000);
|
|
|
|
Pa_Terminate();
|
|
return 0;
|
|
|
|
error:
|
|
Pa_Terminate();
|
|
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
|
fprintf( stderr, "Error number: %d\n", err );
|
|
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
|
return 0;
|
|
}
|