From 3bb280cda254ea00d10ed1742c8bf913612b2569 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Thu, 18 Nov 2021 15:47:49 +0100 Subject: [PATCH] c++ audio player --- pyaudioplayeralsa/CMakeLists.txt | 6 ++ pyaudioplayeralsa/main.cpp | 123 ++++++++++++++++++++++++++++++ pyaudioplayeralsa/plan.md | 5 ++ pyaudioplayeralsa/src/WavFile.cpp | 0 pyaudioplayeralsa/src/WavFile.h | 61 +++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 pyaudioplayeralsa/CMakeLists.txt create mode 100644 pyaudioplayeralsa/main.cpp create mode 100644 pyaudioplayeralsa/plan.md create mode 100644 pyaudioplayeralsa/src/WavFile.cpp create mode 100644 pyaudioplayeralsa/src/WavFile.h diff --git a/pyaudioplayeralsa/CMakeLists.txt b/pyaudioplayeralsa/CMakeLists.txt new file mode 100644 index 0000000..5bd7f6d --- /dev/null +++ b/pyaudioplayeralsa/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.16) + +project("pyaudioplayeralsa") + +add_executable(play main.cpp src/WavFile.cpp) +target_link_libraries(play -lasound) \ No newline at end of file diff --git a/pyaudioplayeralsa/main.cpp b/pyaudioplayeralsa/main.cpp new file mode 100644 index 0000000..77e57f9 --- /dev/null +++ b/pyaudioplayeralsa/main.cpp @@ -0,0 +1,123 @@ +/* + * Simple sound playback using ALSA API and libasound. + * + * Compile: + * $ cc -o play sound_playback.c -lasound + * + * Usage: + * $ ./play < + * + * Examples: + * $ ./play 44100 2 5 < /dev/urandom + * $ ./play 22050 1 8 < /path/to/file.wav + * + * Copyright (C) 2009 Alessandro Ghedini + * -------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Alessandro Ghedini wrote this file. As long as you retain this + * notice you can do whatever you want with this stuff. If we + * meet some day, and you think this stuff is worth it, you can + * buy me a beer in return. + * -------------------------------------------------------------- + */ + +#include +#include + +#include + +#include "src/WavFile.h" + +#define PCM_DEVICE "default" + +int main(int argc, char **argv) +{ + std::ifstream wavFileStream("test.wav", std::ios::binary); + WavFile wav(wavFileStream); + wavFileStream.close(); + + unsigned int pcm, tmp, dir; + snd_pcm_t *pcm_handle; + snd_pcm_hw_params_t *params; + snd_pcm_uframes_t frames; + char *buff; + int buff_size, loops; + + /* Open the PCM device in playback mode */ + if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, + SND_PCM_STREAM_PLAYBACK, 0) < 0) + printf("ERROR: Can't open \"%s\" PCM device. %s\n", + PCM_DEVICE, snd_strerror(pcm)); + + /* Allocate parameters object and fill it with default values*/ + snd_pcm_hw_params_alloca(¶ms); + + snd_pcm_hw_params_any(pcm_handle, params); + + /* Set parameters */ + if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params, + SND_PCM_ACCESS_RW_INTERLEAVED) < 0) + printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm)); + + if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params, + SND_PCM_FORMAT_S16_LE) < 0) + printf("ERROR: Can't set format. %s\n", snd_strerror(pcm)); + + if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, wav.channels()) < 0) + printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm)); + + unsigned int rate = wav.sampleRate(); + if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0) + printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm)); + + /* Write parameters */ + if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0) + printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm)); + + /* Resume information */ + printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle)); + + printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle))); + + snd_pcm_hw_params_get_channels(params, &tmp); + printf("channels: %i ", tmp); + + if (tmp == 1) + printf("(mono)\n"); + else if (tmp == 2) + printf("(stereo)\n"); + + snd_pcm_hw_params_get_rate(params, &tmp, 0); + printf("rate: %d bps\n", tmp); + + /* Allocate buffer to hold single period */ + snd_pcm_hw_params_get_period_size(params, &frames, 0); + + buff_size = frames * wav.channels() * 2 /* 2 -> sample size */; + buff = (char *)malloc(buff_size); + + snd_pcm_hw_params_get_period_time(params, &tmp, NULL); + + size_t wavFilePosition = 0; + while (wavFilePosition < wav.size()) + { + size_t dataToWrite = std::min(size_t(buff_size), wav.size() - wavFilePosition); + int framesToWrite = dataToWrite / wav.channels() / 2; + if (pcm = snd_pcm_writei(pcm_handle, wav[wavFilePosition], framesToWrite) == -EPIPE) + { + printf("XRUN.\n"); + snd_pcm_prepare(pcm_handle); + } + else if (pcm < 0) + { + printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm)); + } + wavFilePosition += dataToWrite; + } + + snd_pcm_drain(pcm_handle); + snd_pcm_close(pcm_handle); + free(buff); + + return 0; +} \ No newline at end of file diff --git a/pyaudioplayeralsa/plan.md b/pyaudioplayeralsa/plan.md new file mode 100644 index 0000000..816576d --- /dev/null +++ b/pyaudioplayeralsa/plan.md @@ -0,0 +1,5 @@ +- play wav file + - read & parse wav file +- stop wave file in the middle, wait 2 secs and continue +- fade in/out +- mix second wave file on top (some effect) diff --git a/pyaudioplayeralsa/src/WavFile.cpp b/pyaudioplayeralsa/src/WavFile.cpp new file mode 100644 index 0000000..e69de29 diff --git a/pyaudioplayeralsa/src/WavFile.h b/pyaudioplayeralsa/src/WavFile.h new file mode 100644 index 0000000..40f03a7 --- /dev/null +++ b/pyaudioplayeralsa/src/WavFile.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +template +std::string readStr(std::istream &str) +{ + char buff[len]; + str.read(buff, len); + return std::string(buff, len); +} + +template +T read(std::istream &str) +{ + T res; + str.read((char *)&res, sizeof(res)); + return res; +} + +class WavFile +{ +public: + WavFile(std::istream &str) + { + auto chunkId = readStr<4>(str); + auto chunkSize = read(str); + auto format = readStr<4>(str); + + auto subchunk1Id = readStr<4>(str); + auto subchunk1Size = read(str); + + auto audioFormat = read(str); + numChannels_ = read(str); + sampleRate_ = read(str); + auto byteRate = read(str); + + auto blockAlign = read(str); + bitsPerSample_ = read(str); + + auto subchunk2Id = readStr<4>(str); + dataSize_ = read(str); + + data_ = std::unique_ptr(new char[dataSize_]); + str.read(data_.get(), dataSize_); + } + + uint32_t sampleRate() const { return sampleRate_; } + uint16_t channels() const { return numChannels_; } + uint32_t size() const { return dataSize_; } + + const char *operator[](int offset) const { return &data_.get()[offset]; } + +private: + std::unique_ptr data_; + uint32_t sampleRate_; + uint16_t bitsPerSample_; + uint32_t dataSize_; + uint16_t numChannels_; +}; \ No newline at end of file