It is currently Fri Oct 20, 2017 9:17 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: sound question
PostPosted: Sat May 28, 2005 7:53 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
i know this is not an emulation question, but which is the best way of daling with directsound, cos i create soundbuffers (not primary) i lock, copymemory to the buffer pointer, unlock and play..

it seems this give me problems sound has glitches..

any help?

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 28, 2005 9:11 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
basic step-by-step:

1) DirectSoundCreate
2) SetCooperativeLevel
3) CreateSoundBuffer (primary buffer)
4) SetFormat (primary buffer)
5) CreateSoundBuffer (secondary buffer)
6) Flush Secondary buffer (lock, clear, unlock)
7) Fill Secondary buffer (lock, write sound data, unlock)
8) Play (secondary buffer -- with DSBPLAY_LOOPING)
9) Constantly poll play cursor with GetCurrentPosition and track how much of the buffer is emptied. When you have enough empty space to write more sound data:
10) Write new sound data to secondary buffer (lock, write, unlock)
11) Repeat steps 9 and 10 for as long as you want sound playing
12) Stop (secondary buffer)
13) Release (secondary buffer)
14) Release (primary buffer)
15) Release (Direct Sound object)


you will have to keep track of where in the buffer you want to write to (step 10). You do not have to stop the buffer to lock it (and you shouldn't if you want flowing sound).

Flushing the buffer (step 6) is optional -- if you fill it with sound data before you play it, you don't need to flush it. You can skip step 7 and just start playing the secondary buffer (but if you do this you will have to flush the buffer or it will make a god awful noise).

Always be sure to check for the DSERR_BUFFERLOST return value and call Restore when needed. If the user switches to another app and causes your DirectSound to collapse on itself and you don't correct it, the user will be forced to restart your emu to get sound back.

I made a C++ wrapper class which does a decent job of fronting all this stuff -- only thing is it always assumes 16-bit sound (no 8-bit). You can use it if you please, or you can just use it as an example:

http://www.geocities.com/disch_/SoundOut.zip


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 28, 2005 10:11 pm 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
OK i understand nearly all the steps, but what do you mean with flush?
does it mean that i have to clear (set to 0) the sound buffer?

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 8:27 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Quite interesting, Disch. I'm looking for a decent sound driver to replace Allegro... I'm going to give a try too... ;)

_________________
Zepper
RockNES developer


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 9:19 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Anes: yeah, you'd fill the sound buffer with silence (for 16-bit sound this would be 0, but for 8-bit sound this would be $80)


Fx3: Feel free to use. It's Windows-only though (DirectSound), and it only does 16-bit sound in mono or stereo (you could add 8-bit sound easily enough though -- just have to make minor changes to SetFormat and Flush).

I probably should have made a readme or something for that thing... anyway:

1) Initialize
2) SetFormat
3) Start
4) continually call CanWrite to see how many bytes of sound you can write to the buffer
5) call Lock; 'len' is the number of bytes you want to lock, -1 will lock as much as possible (will use what's returned from CanWrite)
6) write to sound buffers
7) call Unlock, and tell it how many bytes you actually wrote to the buffer(s).
8) Repeat steps 4-7 to stream sound
9) Stop
10) Destroy


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 9:22 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
It's been a long time since I switched to 16-bit output... ;) No 8-bit sound is required. I'll let you know anything new..

_________________
Zepper
RockNES developer


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 11:38 am 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
More question on you step-by-step:

step 9: should i do dwWriteCursor - dwPlayCursor
step 10: lock it with DSBLOCK_FROMWRITECURSOR?

i make you all these question cos im a beginner in DSound and i really dont know how to handle it.

Thanks in advance

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 1:34 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
When you lock, should you always use -1, or what? When you lock, what does that do? If I locked 3 bytes, would they be the first 3 free samples in the buffer and I'd only be able to write 3 samples?


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 29, 2005 2:45 pm 
http://nintencer.fobby.net/sexyal-29may2005.zip

You can use this small library to help you if you'd like.

Instruct your C compiler to define HAVE_DIRECTSOUND. Compile sexyal.c, smallc.c, convert.c, and drivers/dsound.c.

You will need to write floating-point PCM audio data, in the range of [0:1](0.0000... through 1.0000..., inclusive).

You can have 1 channel(mono) or 2 channels(stereo, interleaved).

Example code:

#include "sexyal/sexyal.h"

static SexyAL *Interface;
static SexyAL_device *Output;
static SexyAL_format format;
static SexyAL_buffering buffering;
static SexyAL_enumtype *DriverTypes;
static int CurDriverIndex = 0;

uint32 GetMaxSound(void)
{
return(buffering.totalsize);
}

uint32 GetWriteSound(void)
{
return(Output->CanWrite(Output));
}

void WriteSound(float *Buffer, int Count)
{
//printf("%d\n",Output->CanWrite(Output));
Output->Write(Output, Buffer, Count);
}

int InitSound(int *rate, int channels)
{
memset(&format,0,sizeof(format));
memset(&buffering,0,sizeof(buffering));

Interface = (SexyAL *)SexyAL_Init(0);
DriverTypes = Interface->EnumerateTypes(Interface);

format.sampformat=SEXYAL_FMT_PCMFLOAT;
format.channels=channels;
format.rate=*rate;
buffering.fragcount=buffering.fragsize=0;
buffering.ms=soundbufsize;

if(!(Output=Interface->Open(Interface,NULL,&format,&buffering, DriverTypes[CurDriverIndex].type)))
{
Interface->Destroy(Interface);
Interface=0;
return(0);
}

format.sampformat=SEXYAL_FMT_PCMFLOAT;
format.channels=channels;
format.byteorder=0;

Output->SetConvert(Output,&format);

*rate = format.rate
return(1);
}

int KillSound(void)
{
if(Output)
Output->Close(Output);
if(Interface)
Interface->Destroy(Interface);
Interface=0;
if(!Output) return(0);
Output=0;
return(1);
}


Top
  
 
 Post subject:
PostPosted: Sun May 29, 2005 3:27 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Anes:

The DirectSound buffer is conceptually circular. DSound will start from a point on the buffer and move slowly around the cirlcle playing the sound data if finds. That point is marked as the Play Cursor. The Write Cursor is always just a little bit ahead of the Play Cursor -- its purpose is to signal where it's safe to write. The area between the Play and Write cursors is 'unsafe' to write to when the surface is playing (although that's hard to avoid sometimes -- like if your program is running behind or if your latency is too low. Worst thing that will happen is the sound will crackle or get distorted -- which would happen anyway under those circumstances. So yeah try to avoid writing between the cursors, but it isn't the end of the world if you do.)

Your job is to follow the Play cursor so that as it plays the sound data, you write new sound data to the buffer, so that when the play cursor makes it around full circle, it won't play data it already played -- it will play new sound data (streaming the sound smoothly)

I made an example in paint which outlines the concept:

http://www.geocities.com/disch_/ds.png

P = is the Play Cursor
W = is the Write Cursor
X = is where your program will want to write to next (DSound does not track this! You will have to track it on your own)

The green area of the circle is sound data that is ready to play. The red area is what you should avoid writing to, and the yellow area is sound data that has already played and should be replaced (if the Play Cursor hits the yellow portion it will play sound it already played, which will make your computer sound like a very fast broken record)


Drag:

Already answered this on IRC, but yeah. When you lock 3 samples, you can only write to those 3 samples on the buffer. In my example code when you specify -1 for the length to lock, it will lock as much as can be written (it will call CanWrite to see how many byte to lock).


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 5:26 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Disch, you didn't answer about the parameters of:
void CSoundOut::Lock(int len, void** bufa,DWORD* siza,void** bufb,DWORD* sizb)

Two buffers... Could you clarify it?

_________________
Zepper
RockNES developer


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 7:20 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Assume you have a sound buffer that's 1000 bytes in size. And let's say you lock 200 bytes starting at the 900'th byte in the buffer. That would go past the end of the buffer -- but since the buffer is conceptually circular, you would want it to wrap around to the start of the buffer. When this happens, something kind of like the following would happen:

http://www.geocities.com/disch_/ds2.png

The green area of the bottom figure represents which area you're trying to lock with the above example numbers -- as you can see, it is two seperate areas of the buffer. Because it's two seperate areas, you need two seperate pointers.

bufa is the pointer to the first section to fill (labeled A on that figure)
siza is the number of bytes you can write to bufa
bufb is the pointer to the second section (labeled B on that figure)
sizb is the number of bytes you write to bufb

fill bufa first, then bufb (if it needs filling).


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 10:44 am 
Offline
User avatar

Joined: Tue Dec 21, 2004 8:35 pm
Posts: 600
Location: Argentina
Disch:

well tell me if what im doing is right (cos i wanna write my own interface, thats cos im not using yours, althought it help me a lot :) )

when the buffer is "wrapped" i write to where to points write cursor (W) until the end of buffer, and then i write the remainder (its well written? i mean in english) bytes at the beginning of the buffer the size it left.

I always check if write buffer + buffersize to write will not split, if not split i write all the buffersize (pointer 1 as you defined) and if its wraped i split the buffer and write to, you know what i mentioned above.

The sound its better for me now, but it still have some glitches.

One Question:
DX docs says that one should not write between the play and write cursors

could be that my problem?

by the way.. do you have a basic step-by-step on how to apply synth band limit for people like me dont doesnt understand to much (and dont wanna to read an entire book about the topic and.. dont understand what blargg says in his page?)

thanks..

_________________
ANes


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 11:08 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Anes, look at this example:

Buffer size = 1024 bytes (your sound buffer, okay?)

The 1st update needs 200 bytes (or samples), and the current pointer to buffer is at 900. It will overflow 1024, agree? So, you take bufferA as 900 (size=124), then bufferB as 0 (beginning, size=100). Got it? :) Anyway, contact me by msn.

_________________
Zepper
RockNES developer


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 11:35 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
I can't really follow what you're doing so I can't say what you're doing wrong -- although maybe I could try to explain things a little clearer.

I refer once again to this diagram:
http://www.geocities.com/disch_/ds.png


You should not be writing your sound at the write cursor. If that's what you're doing you'll have underrun problems. The write cursor is always placed just a bit ahead of the play cursor -- which means the data you write to the write cursor will be played almost immediately! (bad)

You want to keep as much distance between the play cursor and the sound you're writing as possible. If the play cursor ever catches up to where you're writing (X in above diagram... NOT W), you'll suffer from what's known as buffer underrun -- and what will happen is that sound which already has played will play again (making your app sound like a broken record)

The write cursor has ABSOLUTLY NO VALUE other than letting you know where you shouldn't write (you shouldn't write between the play and write cursors -- the red highlighted portion in my above diagram). It should not be any indicator of where you're writing sound data -- you should be writing your sound data WELL AHEAD of the write cursor.

Lemme try and explain the diagram a little better:

Green Area on circle = fresh sound data (what is ready to play)
Yellow Area on circle = stale sound data (has already played -- you don't want it to play again)
Red Area of circle = "off limits" area -- don't write here (just the area between P and W).

Circle 1 shows how things should look when you first start playing your buffer. It's completely filled with sound, and ready to play.

After you start playing the buffer, P and W will start moving clockwise around the circle, playing the sound data (turning green to yellow). This is shown by Circle 2.

Your program's job is to keep an eye on how much of that sound has turned yellow, and to keep replacing the yellow with green (writing new sound data up to the play cursor). The 'X' mark on the circle is the point you last wrote you (DirectSound does not keep track of this!! You will have to do it yourself! This is not the write cursor!). You will end up sort of having 'X' follow 'P' around the circle, replacing the stale yellow sound data with fresh new green sound data. This is shown by Circle 3

As you can see from the diagram, there should always be a decent amount of green space between P and X. If P ever hits a yellow portion, your sound will distort.

As for the buffer wrapping, this just happens (it's not shown in that diagram). All you have to do to manage this is use both pointers you get from DSound. First you fill the buffer1 pointer you get from Lock, then buffer2.

The wrapping doesn't have to have any affect on which areas you lock! Just lock with a starting point and a length like you would for any other lock -- and if you get two pointers, so be it. Just write to both of them.


As for BL-synth, I'm not really in the mood to try and explain it right now. Though I understand that Blargg's docs can be a little difficult to understand when you're new to the topic.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group