I am trying to get sound from simple tapping keyboard. Looks like a little drum machine.
If DirectSound is not a proper way to do this, please suggest something else.
In my code I don't know what's wrong. Here it is without error checking and with translations:
//Declaring the IDirectSound object
IDirectSound* device;
DirectSoundCreate(NULL, &device, NULL);
device->SetCooperativeLevel(hWnd, DSSCL_NORMAL );
/* Declaring secondary buffers */
IDirectSoundBuffer* kickbuf;
IDirectSoundBuffer* snarebuf;
/* Declaring .wav files pointers
And to structures for reading the information int the begining of the .wav file */
FILE* fkick;
FILE* fsnare;
sWaveHeader kickHdr;
sWaveHeader snareHdr;
The structure sWaveHeader is declared this way:
typedef struct sWaveHeader
{
char RiffSig[4]; // 'RIFF'
unsigned long WaveformChunkSize; // 8
char WaveSig[4]; // 'WAVE'
char FormatSig[4]; // 'fmt '
unsigned long FormatChunkSize; // 16
unsigned short FormatTag; // WAVE_FORMAT_PCM
unsigned short Channels; // Channels
unsigned long SampleRate;
unsigned long BytesPerSec;
unsigned short BlockAlign;
unsigned short BitsPerSample;
char DataSig[4]; // 'data'
unsigned long DataSize;
} sWaveHeader;
The .wav file opening
#define KICK "D:/muzic/kick.wav"
#define SNARE "D:/muzic/snare.wav"
fkick = fopen(KICK, "rb")
fsnare = fopen(SNARE, "rb")
Here I make a function that does the common work for snarebuf* and **kickbuf
int read_wav_to_WaveHeader (sWaveHeader* , FILE* , IDirectSoundBuffer* ); // The declaring
But I wil not write this function, just show the way it works with kickbuf, for instance.
fseek(fkick, 0, SEEK_SET); // Zero the position in file
fread(&kickHdr, 1, sizeof(sWaveHeader), fkick); // reading the sWaveHeader structure from file
Here goes a checking for fitting if sWaveHeader structure:
if(memcmp(pwvHdr.RiffSig, "RIFF", 4) ||
memcmp(pwvHdr.WaveSig, "WAVE", 4) ||
memcmp(pwvHdr.FormatSig, "fmt ", 4) ||
memcmp(pwvHdr.DataSig, "data", 4))
return 1;
Declaring the format and descriptor for a buffer and filling them:
DSBUFFERDESC bufDesc;
WAVEFORMATEX wvFormat;
ZeroMemory(&wvFormat, sizeof(WAVEFORMATEX));
wvFormat.wFormatTag = WAVE_FORMAT_PCM;
wvFormat.nChannels = kickHdr.Channels;
wvFormat.nSamplesPerSec = kickHdr.SampleRate;
wvFormat.wBitsPerSample = kickHdr.BitsPerSample;
wvFormat.nBlockAlign = wvFormat.wBitsPerSample / 8 * wvFormat.nChannels;
ZeroMemory(&bufDesc, sizeof(DSBUFFERDESC));
bufDesc.dwSize = sizeof(DSBUFFERDESC);
bufDesc.dwFlags = DSBCAPS_CTRLVOLUME |
DSBCAPS_CTRLPAN |
DSBCAPS_CTRLFREQUENCY;
bufDesc.dwBufferBytes = kickHdr.DataSize;
bufDesc.lpwfxFormat = &wvFormat;
Well, the creating of a buffer:
device->CreateSoundBuffer(&bufDesc, &kickbuf, NULL); // Any mistakes by this point?
Now locking the buffer and loading some data to it.
This data starts after sizeof(sWaveHeader) bytes in a WAVE file, am I wrong?
LPVOID Ptr1; // pointer on a pointer on a First block of data
LPVOID Ptr2; // pointer on a pointer on a Second block of data
DWORD Size1, Size2; // their sizes
Now calling the Lock() method:
kickbuf->Lock((DWORD)LockPos, (DWORD)Size,
&Ptr1, &Size1,
&Ptr2, &Size2, 0);
Loading data (is it ok?):
fseek(fkick, sizeof(sWaveHeader), SEEK_SET);
fread(Ptr1, 1, Size1, fkick);
if(Ptr2 != NULL)
fread(Ptr2, 1, Size2, fkick);
Unlocking the buffer:
kickbuf->Unlock(Ptr1, Size1, Ptr2, Size2);
Setting the volume:
kickbuf->SetVolume(-2500);
Then I make a wile(1) looping:
1. ask for a key pressing
2. if it is pressed:
kickbuf->SetCurrentPosition(0)
kickbuf->Play(0,0,0);
But there's no sound playing, please say, what is not proper in my code or maybe in the whole concept. Thank you.
When you initialize the WAVEFORMATEX, your are forgetting to set the nAvgBytesPerSec member. Add this line after the initialization of wvFormat.nBlockAlign:
wvFormat.nAvgBytesPerSec = wvFormat.nSamplesPerSec * wvFormat.nBlockAlign;
Also, I suspect this could be a problem:
kickbuf->SetVolume(-2500);
I suspect that will just attenuate your sample to absolute silence. Try taking that call out so that it plays at full volume.
But more likely, none of you sample code above shows validation of the return values from any of the DirectSound APIs, nor any of the file I/O values. Have you validated the HRESULTs returned by all the DSound APIs are returning S_OK? Have you tried printing or using OutputDebugString to print the values you computed for the members of WAVEFORMATEX?
Have you debugging the fread calls to validate that you are getting valid data into your buffers?
Hope this helps.
Related
I've got a simple wav header reader i found online a long time ago, i've gotten back round to using it but it seems to replace around 1200 samples towards the end of the data chunk with a single random repeated number, eg -126800. At the end of the sample is expected silence so the number should be zero.
Here is the simple program:
void main() {
WAV_HEADER* wav = loadWav(".\\audio\\test.wav");
double sample_count = wav->SubChunk2Size * 8 / wav->BitsPerSample;
printf("Sample count: %i\n", (int)sample_count);
vector<int16_t> samples = vector<int16_t>();
for (int i = 0; i < wav->SubChunk2Size; i++)
{
int val = ((wav->data[i] & 0xff) << 8) | (wav->data[i + 1] & 0xff);
samples.push_back(val);
}
printf("done\n");
}
And here is the Wav reader:
typedef struct
{
//riff
uint32_t Chunk_ID;
uint32_t ChunkSize;
uint32_t Format;
//fmt
uint32_t SubChunk1ID;
uint32_t SubChunk1Size;
uint16_t AudioFormat;
uint16_t NumberOfChanels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlignment;
uint16_t BitsPerSample;
//data
uint32_t SubChunk2ID;
uint32_t SubChunk2Size;
//Everything else is data. We note it's offset
char data[];
} WAV_HEADER;
#pragma pack()
inline WAV_HEADER* loadWav(const char* filePath)
{
long size;
WAV_HEADER* header;
void* buffer;
FILE* file;
fopen_s(&file,filePath, "r");
assert(file);
fseek(file, 0, SEEK_END);
size = ftell(file);
rewind(file);
std::cout << "Size of file: " << size << std::endl;
buffer = malloc(sizeof(char) * size);
fread(buffer, 1, size, file);
header = (WAV_HEADER*)buffer;
//Assert that data is in correct memory location
assert((header->data - (char*)header) == sizeof(WAV_HEADER));
//Extra assert to make sure that the size of our header is actually 44 bytes
assert((header->data - (char*)header) == 44);
fclose(file);
return header;
}
Im not sure what the problem is, i've confirmed that there is no meta data, nor is there a mis match between the numbers read from the header of the file and the actual file. Im assuming its a size/offset misallignment on my side, but i cannot see it.
Any help welcomed.
Sulkyoptimism
WAV is just a container for different audio sample formats.
You're making assumptions on a wav file that would have been OK on Windows 3.11 :) These don't hold in 2021.
Instead of rolling your own Wav file reader, simply use one of the available libraries. I personally have good experiences using libsndfile, which has been around roughly forever, is very slim, can deal with all prevalent WAV file formats, and with a lot of other file formats as well, unless you disable that.
This looks like a windows program (one notices by the fact you're using very WIN32API style capital struct names – that's a bit oldschool); so, you can download libsndfile's installer from the github releases and directly use it in your visual studio (another blind guess).
Apple (macOS and iOS) software often does not create WAVE/RIFF files with just a canonical Microsoft 44-byte header at the beginning. Those Wave files can instead can use a longer header followed by a padding block.
So you need to use the full WAVE RIFF format parsing specification instead of just reading from a fixed size 44 byte struct.
I am reading a collection file (20 or so small files in one) with fread_s and the content is being written in a struct. Like 99% of the times it reads the data correctly, but one time, at always the same position it seems to ignore the byte size of the element size parameter and just reads 500 or so bytes until it aborts and reports a feof error. The thing is, it doesn't even write the last three bytes of the int to the struct.
When I remove the checks, and let it continue reading, it will read normal again, like nothing happened.
I observed that the _Placeholder variable in the file pointer gets changed to a different value, and then back again, but I guess its just the eof error getting packed in there.
#pragma pack(push, 1)
struct fileHeader {
__int32 typeID;
bool isGFX;
char filename[8];
__int32 offset;
};
#pragma pack(pop)
#define HEADERSIZE 68
#define FILEHEADERSIZE 17
....
FILE *file;
fopen_s(&file, filename.c_str(), "r");
for (int i = 0; i < header.files - 1; i++) {
fseek(file, HEADERSIZE + i * FILEHEADERSIZE, 0);
fileHeader headerFile;
memset(&headerFile, 0, FILEHEADERSIZE);
int oldPointer = ftell(file); //118
int d = fread_s(&headerFile, FILEHEADERSIZE, FILEHEADERSIZE, 1, file); //returns 0
int newPointer = ftell(file); //630
int e = errno; //0
int ea = ferror(file); //0
int ef = feof(file); //1
//getting used here in a function
}
headerFile = {typeID=17 isGFX=true filename=0x00fefd05 "CURSORR" offset = 164} - offset should be 6820
Like Jonathan Leffler said in the comments, the mistake was, that I didn't read in binary mode. a simple change fopen_s(&file, filename.c_str(), "r"); to fopen_s(&file, filename.c_str(), "rb"); fixed the problem.
i am trying to do some functionality with espeak but missing some parameters
(i don`t know it) and working on code blocks on Linux
the next code runs well and reads Arabic Text
`#include<string.h>
#include<malloc.h>
#include</usr/local/include/espeak/speak_lib.h>
int main(int argc, char* argv[] )
{
char text[] = {"الله لطيف "};
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0 );
espeak_SetVoiceByName("ar");
unsigned int size = 0;
while(text[size]!='\0') size++;
unsigned int flags=espeakCHARS_AUTO | espeakENDPAUSE;
espeak_Synth( text, size+1, 0,POS_CHARACTER,0, flags, NULL, NULL );
espeak_Synchronize( );
return 0;
}`
now could you help us finding these parameters from Espeak
1.Fuction which return the generated wave to store it in a variable
2.Frequency
3.number of channels
4.sample size
5.a buffer in which we store samples
6.number of samples
If you can't find a suitable example, you will have to read the documentation in the header file. Haven't used it, but it looks pretty comprehensible:
http://espeak.sourceforge.net/speak_lib.h
When you called espeak_Initialize you passed in AUDIO_OUTPUT_PLAYBACK. You will need to pass in AUDIO_OUTPUT_RETRIEVAL instead, and then it looks like you must call espeak_SetSynthCallback with a function of your own creation to accept the samples.
Your adapted code would look something like this (UNTESTED):
#include <string.h>
#include <vector>
#include </usr/local/include/espeak/speak_lib.h>
int samplerate; // determined by espeak, will be in Hertz (Hz)
const int buflength = 200; // passed to espeak, in milliseconds (ms)
std::vector<short> sounddata;
int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) {
if (wav == NULL)
return 1; // NULL means done.
/* process your samples here, let's just gather them */
sounddata.insert(sounddata.end(), wav, wav + numsamples);
return 0; // 0 continues synthesis, 1 aborts
}
int main(int argc, char* argv[] ) {
char text[] = {"الله لطيف "};
samplerate = espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, buflength, NULL, 0);
espeak_SetSynthCallback(&SynthCallback);
espeak_SetVoiceByName("ar");
unsigned int flags=espeakCHARS_AUTO | espeakENDPAUSE;
size_t size = strlen(text);
espeak_Synth(text, size + 1, 0, POS_CHARACTER, 0, flags, NULL, NULL);
espeak_Synchronize();
/* in theory sounddata holds your samples now... */
return 0;
}
So for your questions:
Function which return the generated wave to store it in a variable - You write a callback function, and that function gets little buflength-long bits of the wav to process. If you are going to accumulate the data into a larger buffer, I've shown how you could do that yourself.
Frequency - Through this API it doesn't look like you pick it, espeak does. It's in Hz and returned as samplerate above.
Number of Channels - There's no mention of it, and voice synthesis is generally mono, one would think. (Vocals are mixed center by default in most stereo mixes...so you'd take the mono data you got back and play the same synthesized data on left and right channels.)
Sample Size - You get shorts. Those are signed integers, 2 bytes, range of -32,768 to 32,767. Probably it uses the entire range, doesn't seem to be configurable, but you could test and see what you get out.
A Buffer In Which We Store Samples - The synthesis buffer appears to belong to espeak, which handles the allocation and freeing of it. I've shown an example of using a std::vector to gather chunks from multiple calls.
Number of Samples - Each call to your SynthCallback will get a potentially different number of samples. You might get 0 for that number and it might not mean it's at the end.
I need to create a custom reading callback function that can read contents of a file in the form of a std::string into a uint8_t * buf. I tried multiple different methods found around the internet and on stackoverflow but sometimes it works and other the the program infinitely loops or stops execution half way.
I have no problems with amr/3gp files but all wav/pcm files are causing some problems for some reason. All I know its something to do with the reading function I have so far.
Ideally I would like to be able to give the program any type of file and then it converts it.
This is how I am calling the readCallback function from the code:
//create the buffer
uint8_t * avio_ctx_buffer = NULL;
//allocate space for the buffer using ffmpeg allocation method
avio_ctx_buffer = (uint8_t *) av_malloc(avio_ctx_buffer_size);
//Allocate and initialize an AVIOContext for buffered I/O.
//audio variable contains the contents of the audio file
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0, &audio, &readCallback, NULL, NULL);
Here is the callback function that works on some types of files:
static int readCallback(void* opaque, uint8_t * buf, int buf_size){
std::string * file =static_cast<std::string *>(opaque);
if(file->length() == 0){
return AVERROR_EOF; //if we reach to the end of the string, return
// return End of file
}
// Creating a vector of the string size
std::vector<uint8_t> array(file->length());
//Copying the contents of the string into the vector
std::copy(file->begin(),file->end(),array.begin());
//Copying the vector into buf
std::copy(array.begin(),array.end(),buf);
return file->length();
}
After tyring some stuff for awhile, I got a solution using std::stringstream and it works well with several formats I tested with so far: 3gp/amr,wav/pcm,mp3.
Here a the snippet of code:
//Create a string stream that contains the audio
std::stringstream audio_stream(audio);
//create the buffer
uint8_t * avio_ctx_buffer = NULL;
//allocate space for the buffer using ffmpeg allocation method
avio_ctx_buffer = (uint8_t *) av_malloc(avio_ctx_buffer_size);
//Allocate and initialize an AVIOContext for buffered I/O.
//Pass the stringstream audio_stream
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0,&audio_stream, &readCallback, NULL, NULL);
The callback function:
static int readFunction1(void* opaque, uint8_t * buf, int buf_size){
//Cast the opaque pointer to std::stringstream
std::stringstream * me =static_cast<std::stringstream *>(opaque);
//If we are at the end of the stream return FFmpeg's EOF
if(me->tellg() == buf_size){
return AVERROR_EOF;
}
// Read the stream into the buf and cast it to char *
me->read((char*)buf, buf_size);
//return how many characters extracted
return me->tellg();
}
I want to pass a character buffer to a proc entry (say /proc/my_file) from my program. This character buffer contains my structure's elements which is of the form:
struct my_table { char src_ip[4]; char dest_ip[4]; int out_flag; }my_t;
I assign the elements of my_table and copy its contents to an unsigned char buffer as follows:
memcpy(buffer, &my_t, sizeof(struct my_table));
Then I write the buffer's contents into a proc entry created by me (by the name my_file) as:
write(fd, buffer, sizeof(buffer));
where fd is the file descriptor returned by open() after opening the /proc/my_file with O_WRONLY | O_APPEND flags.
What I couldn't understand is that I can only see the first character string i.e my_t.src_ip in this case to be written to /proc/my_file (did a
cat /proc/my_file
to check the content written) and subsequently I observed that the write() operation to /proc/my_file ends as soon as it encounters a null character in the buffer content.
Can I know why does this happen and how to solve this problem of writing a structure's contents into a /proc entry?
Edit: SSCCE for my question:
The structure:
struct my_iptable {
char protocol[5]; // to check whether the protocol mentioned is tcp/udp/icmp
char src_ip[16]; // source ip address
char dest_ip[16]; // destination ip address
char src_net_mask[16]; // source net mask
char dest_net_mask[16]; // destination net mask
int src_port; // source port number
int dest_port; // destination port number
char action[8]; // either block or unblock
int delete_rule; // gets the rule number to be deleted
int print_flag; // is set if we are asked to print the rules that have been set
int out_flag; // is set if the packet is outbound, else set to 0;
};
The assignment of my_ipt to null:
struct my_iptable my_ipt;
memset(&my_ipt, '\0', sizeof(struct my_iptable));
I have assigned my_ipt's fields properly.
The copying to buffer and writing to proc part:
unsigned char write_buf[sizeof(struct my_iptable)];
memcpy(write_buf, &my_ipt, sizeof(struct my_iptable));
int proc_fp = open("/proc/minifw", O_WRONLY | O_APPEND);
if(proc_fp < 0) {
printf("Couldn't open /proc/minifw for writing\n");
exit(EXIT_FAILURE);}
if(write(proc_fp, write_buf, sizeof(struct my_iptable)) == -1) {
printf("There was an error writing to minifw buffer\n");
exit(EXIT_FAILURE);
}
I hope this gives you appropriate info on what I want to understand.
Thanks!
use sizeof(struct my_table) instead
write(fd, buffer, sizeof(struct my_table));
If your buffer is defined as pointer:
struct my_table *buffer;
then the size of buffer will be equal to the size of pointer (4 for 32-bit systems and 8 for 64-bit systems) and not the real size of the struct my_table