Last active
March 29, 2024 22:33
-
-
Save niuniulla/8e21c590525fe0727f6067dd625191b3 to your computer and use it in GitHub Desktop.
A minimal implementation of audio recording and playback using SDL2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This software is provided 'as-is', without any express or implied | |
warranty. In no event will the authors be held liable for any damages arising | |
from the use of this software. | |
Permission is granted to anyone to use this software for any purpose, | |
including commercial applications, and to alter it and redistribute it | |
freely. | |
*/ | |
/* | |
A minimal implementation of audio recording and playback using SDL2. | |
*/ | |
#include <SDL2/SDL.h> | |
#include <stdlib.h> | |
#include <iostream> | |
static Uint8* buffer = 0; | |
static int recordPos = 0; // buffer position for recording | |
static int playbackPos = 0; // buffer position for playback | |
int gLenghtOfRecordingSecond = 5; // in seconds, length of recording and playback | |
// callback function for recording | |
void recordCallback(void *userdata, Uint8 *stream, int len) { | |
SDL_memcpy(buffer + recordPos, stream, len); // copy data from stream to buffer | |
recordPos += len; | |
} | |
// callback function for playback | |
void playbackCallback(void *userdata, Uint8 *stream, int len) { | |
SDL_memcpy(stream, buffer + playbackPos, len); // copy data from buffer to stream | |
playbackPos += len; | |
} | |
// main function | |
int main() { | |
// init SDL, under circumstance of this application | |
SDL_Init(SDL_INIT_AUDIO); | |
//Get capture device count | |
int deviceCount = SDL_GetNumAudioDevices(SDL_TRUE); | |
// There should have at least 1 device to work | |
if (deviceCount < 1) | |
{ | |
std::cout << "ERR - There is no valid audio devices. SDL error: " << SDL_GetError() << std::endl; | |
return 1; | |
} | |
std::cout << "INFO - There are: " << deviceCount << " recording devices available." << std::endl; | |
// get device names | |
for (int i=0; i<deviceCount; i++) | |
{ | |
std::cout << "INFO - Device" << i << ": " << SDL_GetAudioDeviceName(i, SDL_TRUE) << std::endl; | |
} | |
// select audio device | |
SDL_AudioDeviceID recordingDeviceId = 0; | |
SDL_AudioDeviceID playbackDeviceId = 0; | |
//Default recording spec | |
SDL_AudioSpec desiredRecordingSpec, receivedRecordingSpec; | |
SDL_zero(desiredRecordingSpec); | |
desiredRecordingSpec.freq = 44100; | |
desiredRecordingSpec.format = AUDIO_F32; | |
desiredRecordingSpec.channels = 2; | |
desiredRecordingSpec.samples = 4096; | |
desiredRecordingSpec.callback = recordCallback; | |
//Open recording device | |
int index = deviceCount - 1; // can be any number as long as it is smaller than the available devices | |
recordingDeviceId = SDL_OpenAudioDevice( SDL_GetAudioDeviceName( index, SDL_TRUE ), SDL_TRUE, &desiredRecordingSpec, &receivedRecordingSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE ); | |
if( recordingDeviceId == 0 ) | |
{ | |
std::cout << "ERR - Failed to open recording device. SDL error :" << SDL_GetError() << std::endl; | |
return -1; | |
} | |
// default playing spec | |
SDL_AudioSpec desiredPlaybackSpec, receivedPlaybackSpec; | |
SDL_zero(desiredPlaybackSpec); | |
desiredPlaybackSpec.freq = 44100; | |
desiredPlaybackSpec.format = AUDIO_F32; | |
desiredPlaybackSpec.channels = 2; | |
desiredPlaybackSpec.samples = 4096; | |
desiredPlaybackSpec.callback = playbackCallback; | |
//open playback device | |
playbackDeviceId = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredPlaybackSpec, &receivedPlaybackSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE); | |
if (playbackDeviceId == 0) | |
{ | |
std::cout << "ERR - Failed to open playback device. SDL error: " << SDL_GetError() << std::endl; | |
return -1; | |
} | |
// compute the buffer size for recording | |
// compute bytes per sample = channels * audio bit format / 8 | |
int bytePerSample = receivedRecordingSpec.channels * (SDL_AUDIO_BITSIZE(receivedRecordingSpec.format) / 8); | |
// compute bytes per second = freq * bytes/sample | |
int bytePerSecond = bytePerSample * receivedRecordingSpec.freq; | |
// compte total bytes for buffer | |
int bufferSize = bytePerSecond * gLenghtOfRecordingSecond; | |
// allocate memory for buffer | |
buffer = new Uint8[bufferSize]; | |
// init buffer | |
memset(buffer, 0, bufferSize); | |
std::cout << "INFO - Allocated :" << bufferSize << " bytes for buffer." << std::endl; | |
// recording | |
std::cout << "INFO - Start to record..." << std::endl; | |
// unpause device to make it work, it is paused by default | |
SDL_PauseAudioDevice(recordingDeviceId, SDL_FALSE); | |
// I did a sleep here to wait for the recording to finish | |
SDL_Delay(5050); | |
// pause recording | |
SDL_PauseAudioDevice(recordingDeviceId, SDL_TRUE); | |
std::cout << "INFO - Playing back the recorded..." << std::endl; | |
// unpause device to playback | |
SDL_PauseAudioDevice(playbackDeviceId, SDL_FALSE); | |
// delay to finish playback | |
SDL_Delay(6000); | |
// cleaning | |
if (!buffer) | |
{ | |
delete[] buffer; | |
buffer = nullptr; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment