User Tools

Site Tools


outputting_20real-time_20audio

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

outputting_20real-time_20audio [2018/03/31 13:19]
127.0.0.1 external edit
outputting_20real-time_20audio [2018/04/15 12:32] (current)
richardrussell Added syntax highlighting
Line 1: Line 1:
 =====Outputting real-time audio===== =====Outputting real-time audio=====
  
-//by Richard Russell, November 2006//\\ \\  BBC BASIC for Windows provides a number of methods for outputting sounds and music, for example the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin7.html#​sound|SOUND]] statement and the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin8.html#​play|*PLAY]] command. However there may be circumstances when you want more control over the audio output, such as generating continuous tones of a particular waveform and/or frequency.\\ \\  One way you can go about that is to create a **.WAV** file containing the required audio content, and play that using [[http://​www.bbcbasic.co.uk//​bbcwin/​manual/​bbcwine.html#​playsound|SYS "​PlaySound"​]],​ but that isn't suitable when you need continuous output or real-time control. In those cases you can use the method described in this article, which allows you to create and output audio data directly under the control of your program.\\ \\ +//by Richard Russell, November 2006//\\ \\  BBC BASIC for Windows provides a number of methods for outputting sounds and music, for example the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin7.html#​sound|SOUND]] statement and the [[http://​www.bbcbasic.co.uk/​bbcwin/​manual/​bbcwin8.html#​play|*PLAY]] command. However there may be circumstances when you want more control over the audio output, such as generating continuous tones of a particular waveform and/or frequency.\\ \\  One way you can go about that is to create a **.WAV** file containing the required audio content, and play that using [[http://​www.bbcbasic.co.uk//​bbcwin/​manual/​bbcwine.html#​playsound|SYS "​PlaySound"​]],​ but that isn't suitable when you need continuous output or real-time control. In those cases you can use the method described in this article, which allows you to create and output audio data directly under the control of your program. 
 ==== Preliminaries ==== ==== Preliminaries ====
-\\  ​As is usual for programs accessing the Windows API, it is important to trap errors, and closing the window, so that the necessary '​cleanup'​ operations can take place:\\ \\ + 
 +As is usual for programs accessing the Windows API, it is important to trap errors, and closing the window, so that the necessary '​cleanup'​ operations can take place: 
 + 
 +<code bb4w>
         ON ERROR PROCcleanup : SYS "​MessageBox",​ @hwnd%, REPORT$, 0, 48 : QUIT         ON ERROR PROCcleanup : SYS "​MessageBox",​ @hwnd%, REPORT$, 0, 48 : QUIT
         ON CLOSE PROCcleanup : QUIT         ON CLOSE PROCcleanup : QUIT
-The **PROCcleanup** routine is listed later. You may want to change the error reporting to a different method.\\ \\ +</​code>​ 
 + 
 +The **PROCcleanup** routine is listed later. You may want to change the error reporting to a different method. 
 ==== Selecting the audio format ==== ==== Selecting the audio format ====
-\\  ​The first step is to decide the audio //format// you will use: the principal choices being of sampling rate (the main ones being 11025 Hz, 22050 Hz and 44100 Hz) and number of channels (mono, 1 channel, or stereo, 2 channels). The higher the sampling rate the higher the audio frequency that can be generated, but the more work your software needs to do. Normally you should choose the lowest sampling rate suitable for your application,​ remembering that it needs to be at least **double** the highest audio frequency to be generated (according to the [[http://​en.wikipedia.org/​wiki/​Nyquist_rate|Nyquist criterion]]).\\ \\  You set up the required audio format and open the wave output device as follows:\\ \\ + 
 +The first step is to decide the audio //format// you will use: the principal choices being of sampling rate (the main ones being 11025 Hz, 22050 Hz and 44100 Hz) and number of channels (mono, 1 channel, or stereo, 2 channels). The higher the sampling rate the higher the audio frequency that can be generated, but the more work your software needs to do. Normally you should choose the lowest sampling rate suitable for your application,​ remembering that it needs to be at least **double** the highest audio frequency to be generated (according to the [[http://​en.wikipedia.org/​wiki/​Nyquist_rate|Nyquist criterion]]).\\ \\  You set up the required audio format and open the wave output device as follows: 
 + 
 +<code bb4w>
         DIM Format{wFormatTag{l&,​h&​},​ nChannels{l&,​h&​},​ nSamplesPerSec%,​ \         DIM Format{wFormatTag{l&,​h&​},​ nChannels{l&,​h&​},​ nSamplesPerSec%,​ \
         \          nAvgBytesPerSec%,​ nBlockAlign{l&,​h&​},​ wBitsPerSample{l&,​h&​},​ \         \          nAvgBytesPerSec%,​ nBlockAlign{l&,​h&​},​ wBitsPerSample{l&,​h&​},​ \
Line 23: Line 33:
         SYS "​waveOutOpen",​ ^WaveOut%, _WAVE_MAPPER,​ Format{}, 0, 0, 0 TO ret%         SYS "​waveOutOpen",​ ^WaveOut%, _WAVE_MAPPER,​ Format{}, 0, 0, 0 TO ret%
         IF ret% ERROR 100, "​waveOutOpen failed: "​+STR$~ret%         IF ret% ERROR 100, "​waveOutOpen failed: "​+STR$~ret%
-In this example a sampling rate of 44100 Hz has been selected.\\ \\ +</​code>​ 
 + 
 +In this example a sampling rate of 44100 Hz has been selected. 
 ==== Creating and initialising the buffers ==== ==== Creating and initialising the buffers ====
-\\  ​The next step is to decide how many audio buffers you need and how large they should be. To some extent this is an arbitrary decision, but it will depend on things like //latency// (how much time elapses between your program generating the audio data and the actual sound output) and the amount of work needed to create the audio data.\\ \\  Normally you should have at least three buffers: one outputting the sound, one being filled with audio data by your program, and one spare (the buffers are reused cyclically). It is vitally important that your program can create the audio data quickly enough, otherwise there will be interruptions (clicks and stutters) to the sound output. If there is any variability in the rate at which you can generate the data (for example it depends on disk or network accesses) then you may need to use more and/or larger buffers to 'iron out' the fluctuation,​ but this will necessarily result in greater latency.\\ \\  In the example below the number of buffers is three and the length of each buffer is 1024 samples; at 44100 Hz that implies a total latency of about 70 milliseconds. The code for creating and initialising the buffers is as follows:\\ \\ + 
 +The next step is to decide how many audio buffers you need and how large they should be. To some extent this is an arbitrary decision, but it will depend on things like //latency// (how much time elapses between your program generating the audio data and the actual sound output) and the amount of work needed to create the audio data.\\ \\  Normally you should have at least three buffers: one outputting the sound, one being filled with audio data by your program, and one spare (the buffers are reused cyclically). It is vitally important that your program can create the audio data quickly enough, otherwise there will be interruptions (clicks and stutters) to the sound output. If there is any variability in the rate at which you can generate the data (for example it depends on disk or network accesses) then you may need to use more and/or larger buffers to 'iron out' the fluctuation,​ but this will necessarily result in greater latency.\\ \\  In the example below the number of buffers is three and the length of each buffer is 1024 samples; at 44100 Hz that implies a total latency of about 70 milliseconds. The code for creating and initialising the buffers is as follows: 
 + 
 +<code bb4w>
         nBuffers% = 3         nBuffers% = 3
         SamplesPerBuffer% = 1024         SamplesPerBuffer% = 1024
Line 47: Line 63:
           IF ret% ERROR 100, "​waveOutWrite failed: "​+STR$~ret%           IF ret% ERROR 100, "​waveOutWrite failed: "​+STR$~ret%
         NEXT         NEXT
-Note that in this case the audio buffers are allocated from BASIC'​s heap; you could alternatively use the Windows API to allocate the memory.\\ \\ +</​code>​ 
 + 
 +Note that in this case the audio buffers are allocated from BASIC'​s heap; you could alternatively use the Windows API to allocate the memory. 
 ==== Outputting in real-time ==== ==== Outputting in real-time ====
-\\  ​Once the above code has been executed you need to refill the audio buffers fast enough to keep up with the requested sampling rate. The following code constantly checks whether any of the buffers needs refilling and if so calls the **PROCfillbuffer** routine:\\ \\ + 
 +Once the above code has been executed you need to refill the audio buffers fast enough to keep up with the requested sampling rate. The following code constantly checks whether any of the buffers needs refilling and if so calls the **PROCfillbuffer** routine: 
 + 
 +<code bb4w>
         _WHDR_DONE = 1         _WHDR_DONE = 1
         REPEAT         REPEAT
Line 61: Line 83:
           SYS "​Sleep",​ 1           SYS "​Sleep",​ 1
         UNTIL FALSE         UNTIL FALSE
-In this example the sound generation continues indefinitely,​ but you can terminate the process prematurely if you wish. If you do, don't forget to execute **PROCcleanup** before exiting the program.\\ \\ +</​code>​ 
 + 
 +In this example the sound generation continues indefinitely,​ but you can terminate the process prematurely if you wish. If you do, don't forget to execute **PROCcleanup** before exiting the program. 
 ==== Generating the audio data ==== ==== Generating the audio data ====
-\\  ​Obviously it's only possible to describe this aspect in general terms, because precisely what audio data you need to generate will depend on what the program is designed to do. One of the simplest applications is to generate a single pure (sine wave) tone of a specified frequency. The code below does that, where the frequency (in Hz) is assumed to be in the global variable **Frequency**:​\\ \\ + 
 +Obviously it's only possible to describe this aspect in general terms, because precisely what audio data you need to generate will depend on what the program is designed to do. One of the simplest applications is to generate a single pure (sine wave) tone of a specified frequency. The code below does that, where the frequency (in Hz) is assumed to be in the global variable **Frequency**:​ 
 + 
 +<code bb4w>
         DEF PROCfillbuffer(B%,​ N%)         DEF PROCfillbuffer(B%,​ N%)
         LOCAL I%, D         LOCAL I%, D
Line 73: Line 101:
         NEXT         NEXT
         ENDPROC         ENDPROC
-This code is appropriate for monaural output (one channel) where each audio sample consists of a signed 16-bit value in the range -32767 to +32767. Note that, since BBC BASIC has no 16-bit operations, 32-bit indirection ("​B%!I%"​) is used to write to the audio buffer. As a result it is inevitable that two bytes //after the end of the buffer// are corrupted; therefore it is **essential** that when the buffers are created two extra bytes of memory at the end are allocated.\\ \\ +</​code>​ 
 + 
 +This code is appropriate for monaural output (one channel) where each audio sample consists of a signed 16-bit value in the range -32767 to +32767. Note that, since BBC BASIC has no 16-bit operations, 32-bit indirection ("​B%!I%"​) is used to write to the audio buffer. As a result it is inevitable that two bytes //after the end of the buffer// are corrupted; therefore it is **essential** that when the buffers are created two extra bytes of memory at the end are allocated. 
 ==== Cleaning up ==== ==== Cleaning up ====
-\\  ​When you stop the sound generation, or exit the program, you need to shut down the audio output in a controlled fashion:\\ \\ + 
 +When you stop the sound generation, or exit the program, you need to shut down the audio output in a controlled fashion: 
 + 
 +<code bb4w>
         DEF PROCcleanup         DEF PROCcleanup
         WaveOut% += 0 : IF WaveOut% THEN         WaveOut% += 0 : IF WaveOut% THEN
Line 83: Line 117:
         ENDIF         ENDIF
         ENDPROC         ENDPROC
 +</​code>​
 +
 This code might form part of a larger routine, if there are other things that need to be shut down. This code might form part of a larger routine, if there are other things that need to be shut down.
outputting_20real-time_20audio.txt · Last modified: 2018/04/15 12:32 by richardrussell